Skip to content

Instantly share code, notes, and snippets.

@begedin
Last active March 9, 2017 01:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save begedin/fde8ad4d885b6165e69c39d904445b5c to your computer and use it in GitHub Desktop.
Save begedin/fde8ad4d885b6165e69c39d904445b5c to your computer and use it in GitHub Desktop.

Including the library

Sadly, elements is not a separate library.

It's just a part of the still in development Stripe.js v3, so the only way to include it is to include the whole script in the page somewhere. There is no bower library, no amd/require/commonjs package of any sort.

This is the only way to include it:

<script src="https://js.stripe.com/v3/"></script>

Additionally, the v3 does not cover all the features v2 does, so they recommend us including both for now, until that is the case, which would look like

<script src="https://js.stripe.com/v2/"></script> 
<script src="https://js.stripe.com/v3/"></script>

An ember addon usually includes dependencies by adding a bower package to the project, or outright including the script file in the vendor folder, but this is not the case here.

What we still can do, though, is use the contentFor hook of the addon index file, which can then insert content into the app html head or body.

Our own ember-stripe-service does this for v2:

contentFor: function(type) {
 if (type === 'body') {
  return '<script type="text/javascript" src="https://js.stripe.com/v2/"></script>';
  }
}

This will include as a script inside the body. Looking at the app index.html, it's clear where it goes:

<body>
  {{content-for "body"}}

  <script src="{{rootURL}}assets/vendor.js"></script>
  <script src="{{rootURL}}assets/code-corps-ember.js"></script>

  {{content-for "body-footer"}}
</body>

Turning it into a module

The procedure will give us a Stripe global function/variable, which can be used to instantiate a stripe object by giving it an API key. However, we probably want a bit more than that, so my recommended way to proceed would be to create a service.

This service can have an init function, like any other ember object. The function by default does nothing, but we can override it so that it initializes stripe using something like:

init() {
  let { elements, createToken } = Stripe('our_public_key');
  setProperties(this, { elements, createToken });
}

This should make it so that stripe is initialized on app load, and not every time we need it. From that point, we can use/expand service to use the documented elements features: https://stripe.com/docs/elements/reference

Our ember-stripe-service uses an initializer to initialize and inject a similar service, but I think that's a remnant from the ember 1.x era, and is probably no longer necessary.

Adding a component

Mainly, I believe we'll want a card component. Let's say {{stripe-card}}. On a basic level, it should be a tagName: 'form'. It should contain something like <div role="mount-point"></div>. On didInsertElement() it should do something like:

stripeElements: service(),

didInsertElement() {
  this._super(...arguments);
  
  let elements = get(this, 'stripeElements.elements')();
  let card = elements.create('card');
  
  card.mount(this.$('[role="mount-point"]')[0]);
  
  set(this, 'card', card);
}

Additionally, we should handle the submit event of the component itself (since the component is a form):

submit(event) {
  event.preventDefault();
  
  let card = get(this, 'card');
  let createToken = get(this, 'stripeElements.createToken');
  createToken(card).then((result) => {
    if (result.error) {
      get(this, 'onError')(result.error);
    } else {
      get(this, 'onSuccess')(result.token);
    }
  });
}

The approach above should allow the user to specify an onSuccess and onError callback, while also allowing us to define default callbacks in the component. This is all just out of my head, though, so I'm not sure it works that way exactly. Either way, you get the gist.

The approach above should allow the user to specify an onSuccess and onError callback, while also allowing us to define default callbacks in the component. This is all just out of my head, though, so I'm not sure it works that way exactly. Either way, you get the gist.

Where it goes:

Usually, you'd define and export the component from within ember-stripe-elements/addon/components/stripe-card.js. You'd define the template in ember-stripe-elements/app/templates/components/stripe-card.hbs. Youd also define a ember-stripe-elements/app/components/stripe-card.js which simply does an import/export from the addon folder. I'm not sure if this is still the case, but it used to work that way.

Extending

What I have above are the basics.

We can expand from the basics. For example, we could add error handling by adding an error container to the component template and binding to the stripe card object's change event, then checking if there was an error and setting the error property.

Where to go from there, I'm not sure, but this should be enough for a basic addon and for a meetup. I'd say it's even 90% of a fully functional addon. Past that, it's a matter of how many features you want to support eventually.

Thoughts

Redundancy/colision with ember-stripe-service

The addon will be loading the v3 stripe script. Eventually, this script will have all the features of v2 and will replace it. Since ember-stripe-service loads v2, it might be a good idea for the contentFor hook for both addons to first check if the content being added (the <script> tag) is already there, so it's not added twice. If these two addons will live together, I would not be surprised if they eventually both load the same script.

Trying it out and testing

An addon can have a dummy app inside the test folder. I believe an ember init addon command will setup the project folder so that running ember server will run the dummy app, so it's a good idea to use that one for trying things out and later writing tests. It's been ages since I did this, though, so I'm not sure it works that way anymore.

Put it in a readme

You'll have to figure a few things out - how to run the dummy app, how to test, maybe how to include a dev version in another project using npm link. I would put all that into the addon readme, for future contributors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment