npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@adopted-ember-addons/ember-stripe-elements

v2.0.5

Published

A simple Ember wrapper for Stripe Elements.

Downloads

6,702

Readme


Build Status Latest NPM release Ember Observer Score

ember-stripe-elements

A simple Ember wrapper for Stripe Elements.

Maintainers wanted

If you can spare some time in helping maintain this addon, please let us know in the Discord adopted-ember-addons channel or open an issue.

Features

  • Inject <script src="https://js.stripe.com/v3/"></script> into your application's <body>
  • Initialize Stripe with your publishable key
  • Inject a stripev3 service into your controllers so you can use the functions usually available on the stripe object (see https://stripe.com/docs/stripe-js/reference#the-stripe-object):
    • stripe.elements()
    • stripe.confirmCardPayment()
    • stripe.createToken()
    • stripe.createSource()
    • stripe.createPaymentMethod()
    • stripe.retrieveSource()
    • stripe.paymentRequest()
    • stripe.redirectToCheckout()
    • stripe.retrievePaymentIntent()
    • stripe.handleCardPayment()
    • stripe.handleCardAction()
    • stripe.confirmPaymentIntent()
    • stripe.handleCardSetup()
    • stripe.confirmCardSetup()
    • stripe.retrieveSetupIntent()
    • stripe.confirmSetupIntent()
  • Simple, configurable Ember components like <StripeCard/> (demoed in the gif above)

Installation

ember install @adopted-ember-addons/ember-stripe-elements

Breaking Changes

Version 2.0.0

test helpers need to be imported from '@adopted-ember-addons/ember-stripe-elements/test-support'

Compatibility

  • Ember.js v3.24 or above
  • Ember CLI v3.24 or above
  • Node.js v12 or above

Configuration

Stripe Publishable Key

You must set your publishable key in config/environment.js. Also, stripe options contains optional values that you could configure if you want to.

ENV.stripe = {
  publishableKey: 'pk_thisIsATestKey',
  stripeOptions: {
    stripeAccount: 'acct_test_account',
    locale: 'en',
  },
};

Mocking the Stripe API

You can configure the Stripe API to be mocked instead of loaded from https://js.stripe.com/v3/. This is useful for testing.

ENV.stripe = {
  mock: true,
};

When enabled, a mock Stripe object will be assigned to window.Stripe when your app is initialized.

When using the Stripe mock in tests you will likely need to override the mock's methods according to the needs of your test like so:

this.owner.lookup('service:stripev3').createToken = () => ({ token: { id: 'token' } });

Testing and Simulating User Input

When a {{stripe-element}} is instantiated and in the DOM, the underlying stripeElement is available via the stripev3 service. Calling stripeService.getActiveElements() will return an array of those native stripeElements.

This is primarily useful in testing. Stripe renders an iframe which is mostly inaccessible in a test environment, making simulating user input impossible.

You can fill this gap by making the stripeElement emit compatible events, which is a reasonable simulation of the results when in a test context.

This add-on includes some handy utilities for this purpose that can be imported from stripe-mock.

import StripeMock, { stripeEventUtils } from '@adopted-ember-addons/ember-stripe-elements/test-support';

hooks.beforeEach(() => window.Stripe = StripeMock);

stripeEventUtils.triggerReady(stripeElement);
stripeEventUtils.triggerBlur(stripeElement);
stripeEventUtils.triggerFocus(stripeElement);
stripeEventUtils.triggerIncomplete(stripeElement);
stripeEventUtils.triggerComplete(stripeElement);
stripeEventUtils.triggerError(stripeElement, additionalArgs);
stripeEventUtils.triggerChange(stripeElement, additionalArgs);

Both triggerError and triggerChange accept a second argument that can be used to override the default event attributes provided by this addon.

Note: these will not actually change the content of the Stripe UI, they simply force the stripeElement to emit events that are being listened for. WARNING: These utilities rely on undocumented methods, so this may break in the future. This is only intended for use in a test environment. The events are also not exhaustive, but cover the core user flows.

import StripeMock, { stripeEventUtils } from '@adopted-ember-addons/ember-stripe-elements/test-support';

module('...', function (hooks) {

  hooks.beforeEach(() => window.Stripe = StripeMock);

  test('user enters valid data', function (assert) {

    //...some code rendering a {{stripe element}}

    const [stripeElement] = stripeService.getActiveElements();
    stripeEventUtils.triggerComplete(stripeElement);
    ...
  });
});

Lazy loading

You can configure Stripe.js to lazy load when you need it.

ENV.stripe = {
  lazyLoad: true,
};

When enabled, Stripe.js will not be loaded until you call the load() function on the service. It's best to call this function in a route's beforeModel hook.

// subscription page route

import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';

export default class SubscriptionRoute extends Route {
  @service('stripev3') stripe;

  beforeModel() {
    return this.stripe.load();
  }
}

Note that the load function returns a Promise. By returning this promise you ensure that Stripe is fully loaded before the route procedes to the next model hook.

You can also pass publishableKey and optional stripeOptions to the load function.

this.stripe.load('pk_thisIsATestKey', {
  locale: 'en',
  stripeAccount: 'acct_24BFMpJ1svR5A89k',
});

Components

Basics

Every component will:

  • Accept the same array of options accepted by Stripe Elements
  • Call update on the Stripe element if the options are updated
  • Bubble the proper JavaScript events into actions
  • Mount Stripe's own StripeElement in a <div role="mount-point"> on didInsertElement
  • Unmount on willDestroyElement
  • Provide access to the stripev3 service
  • Have the base CSS class name .ember-stripe-element
  • Have a CSS class for the specific element that matches the component's name, e.g. <EmberStripeCard/> has the class .ember-stripe-card
  • Yield to a block
  • Accept autofocus=true passed directly in the component, e.g. <StripeCard @autofocus={{true}}/>

Every component extends from a StripeElement base component which is not exposed to your application.

Actions

The components bubble up all of the JavaScript events that can be handled by the Stripe Element in element.on() from the Ember component using the following actions:

  • onReady
  • onBlur
  • onChange (also sets/unsets the stripeError property on the component, which can be yielded with the block)
  • onFocus
  • onComplete
  • onError

You could handle these actions yourself, for example:

<StripeCard @onBlur={{this.onBlur}} />

Component types

This addon gives you components that match the different Element types:

Stripe recommends using the their card element - The <StripeCard /> component provides this input.

Additionally Stripe provides the following elements, which you can use to build your own form to collect card details:

  • cardNumber: the card number.
  • cardExpiry: the card's expiration date.
  • cardCvc: the card's CVC number.
  • postalCode: the ZIP/postal code.

These are provided via our <StripeElements /> contextual component, which yields sub-components for each element type:

<StripeElements as |elements|>
  <elements.cardNumber />
  <elements.cardExpiry />
  <elements.cardCvc />
  <elements.postalCode />
</StripeElements>

The <StripeElements /> component is a tagless component, so does not have any classes etc on it.

Elements Options

The <StripeElements /> contextual component ensures all the individual elements are created from the same Stripe Elements object.

If you want to pass options to the Stripe Elements object, pass them to the <StripeElements /> contextual component. For example, when using the single-line card element:

<StripeElements @options={{this.elementOptions}} as |elements|>
  <elements.card @options={{this.cardOptions}} />
</StripeElements>

Or when creating your own form:

<StripeElements @options={{this.elementsOptions}} as |Elements|>
  <Elements.cardNumber @options={{this.cardNumberOptions}} />
  <Elements.cardExpiry />
  <Elements.cardCvc />
<StripeElements/>

When you are creating your own form, you will need access to the Stripe Elements object that links all the individual inputs. To do this, use the onReady action on any one of the components to store the object for use when submitting the form. For example:

import Component from '@glimmer/component';
import { action } from '@ember/object';

export default class FormComponent extends Component {
  stripeElement = null;

  @action
  handleReady(stripeElement) {
    this.stripeElement = stripeElement;
  }

  @action
  handleSubmit(evt) {
    evt.preventDefault();
    this.args.onSubmit(this.stripeElement);
  }
}
<form {{on "submit" this.handleSubmit}}>
  <StripeElements as |Elements|>
    <Elements.cardNumber @onReady={{this.handleReady}} />
    <Elements.cardExpiry />
    <Elements.cardCvc />
    <button type="submit">Submit</button>
  <StripeElements/>
</form>

Block usage with element options

In addition to the simple usage above, like <StripeCard />, you can also yield to a block, which will yield both an stripeError object and the stripeElement itself.

For example, you can choose to render out the stripeError, as below (runnable in our dummy app).

<StripeCard @options={{this.options}} as |stripeElement stripeError|>
  {{#if stripeError}}
    <p class="error">{{stripeError.message}}</p>
  {{/if}}
  <button {{on "click" (fn this.submit stripeElement)}}>Submit</button>
  {{#if this.token}}
    <p>Your token: <code>{{this.token.id}}</code></p>
  {{/if}}
</StripeCard>

Also notice the submit action which passes the stripeElement; you could define this in your controller like so:

import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { tracked } from "@glimmer/tracking";
import { action } from '@ember/object';

export default class SubscriptionController extends Controller {
  @service stripev3;

  options = {
    hidePostalCode: true,
    style: {
      base: {
        color: '#333',
      },
    },
  };

  @tracked token = null;

  @action
  async submit(stripeElement) {
    const { token } = await this.stripe.createToken(stripeElement);
    this.token = token;
  }
}

Note the naming convention stripeElement instead of element, as this could conflict with usage of element in an Ember component.

Styling

Note that you can use CSS to style some aspects of the components, but keep in mind that the styles object of the options takes precedence.

Contributing

Fork this repo, make a new branch, and send a pull request. Please add tests in order to have your change merged.

Installation

git clone [email protected]:adopted-ember-addons/ember-stripe-elements.git
cd ember-stripe-elements
npm install

Running

ember serve

Visit your app at http://localhost:4200.

Running Tests

ember test

Testing autofill in browsers

There are self-signed certs in /ssl that will allow you to test autofill inside of the dummy app (or serve as a blueprint for doing this yourself in your own app).

To run using the self-signed certificate, you must:

  • Add 127.0.0.1 localhost.ssl to your hosts file
  • Run the app with ember serve --ssl
  • Add the certificate to your keychain and trust it for SSL
  • Visit the app at https://localhost.ssl:4200.

Building

ember build

For more information on using ember-cli, visit https://ember-cli.com/.

Contributors

Thanks to @begedin, @snewcomer, @filipecrosk, and @Kilowhisky for your early help on this!