惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cloudbric
Cloudbric
T
Threat Research - Cisco Blogs
Simon Willison's Weblog
Simon Willison's Weblog
AWS News Blog
AWS News Blog
P
Privacy & Cybersecurity Law Blog
H
Help Net Security
云风的 BLOG
云风的 BLOG
G
GRAHAM CLULEY
Spread Privacy
Spread Privacy
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
A
Arctic Wolf
Project Zero
Project Zero
Engineering at Meta
Engineering at Meta
P
Privacy International News Feed
Blog — PlanetScale
Blog — PlanetScale
Stack Overflow Blog
Stack Overflow Blog
M
MIT News - Artificial intelligence
The Register - Security
The Register - Security
Recorded Future
Recorded Future
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
C
Cisco Blogs
PCI Perspectives
PCI Perspectives
Recent Announcements
Recent Announcements
Martin Fowler
Martin Fowler
A
About on SuperTechFans
W
WeLiveSecurity
GbyAI
GbyAI
V
Vulnerabilities – Threatpost
The GitHub Blog
The GitHub Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Check Point Blog
Y
Y Combinator Blog
月光博客
月光博客
Scott Helme
Scott Helme
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google DeepMind News
Google DeepMind News
F
Fortinet All Blogs
U
Unit 42
G
Google Developers Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Threatpost
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Google Online Security Blog
Google Online Security Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Cisco Talos Blog
Cisco Talos Blog
博客园 - 三生石上(FineUI控件)
Hugging Face - Blog
Hugging Face - Blog
MongoDB | Blog
MongoDB | Blog
博客园 - 司徒正美

Minko Gechev's blog

skillgrade Unit Tests for AI Agent Skills You Should Care About AI Generative Development LLM-first Web Framework Reactive framework in ~200 lines of JavaScript Managing Angular Are LLMs going to replace us? Prefetching Heuristics Design Patterns in Open Source Projects - Part II Design Patterns in Open Source Projects - Part I What I learned doing 125 public talks - Part I Dynamic imports solve all the problems, right? 5 Angular CLI Features You Didn't Know About Angular quicklink Preloading Strategy Introducing Bazel Schematics for Angular CLI Building TypeScript Projects with Bazel Joining Google Playing Mortal Kombat with TensorFlow.js. Transfer learning and data augmentation Fast, extensible, configurable, and beautiful linter for Go Introducing Guess.js - a toolkit for enabling data-driven user-experiences on the Web Machine Learning-Driven Bundling. The Future of JavaScript Tooling. JavaScript Decorators for Declarative and Readable Code 3 Tricks For Using Redux and Immutable.js with TypeScript Follow Your Dream Career with Open Source. Personal Story. Redux Anti-Patterns - Part 1. State Management. Faster Angular Applications - Understanding Differs. Developing a Custom IterableDiffer Faster Angular Applications - Part 2. Pure Pipes, Pure Functions and Memoization Faster Angular Applications - Part 1. On Push Change Detection and Immutability Understanding Dynamic Scoping and TemplateRef Implementing a Simple Compiler on 25 Lines of JavaScript Developing Statically Typed Programming Language WebVR for a Gamified IDE 7 Angular Tools That You Should Consider Announcing ngrev - Reverse Engineering Tool for Angular Implementing Angular's Dependency Injection in React. Understanding Element Injectors. Distributing an Angular Library - The Brief Guide Angular in Production Ahead-of-Time Compilation in Angular 2.5X Smaller Angular 2 Applications with Google Closure Compiler Building an Angular Application for Production Implementing the Missing "resolve" Feature of the Angular 2 Router Scalable Single-Page Application Architecture Managing ambient type definitions and dealing with the "Duplicate identifier" TypeScript error Static Code Analysis of Angular 2 and TypeScript Projects Enforcing Best Practices with Static Code Analysis of Angular 2 Projects ViewChildren and ContentChildren in Angular Dynamically Configuring the Angular's Router Angular 2 Hot Loader Lazy Loading of Route Components in Angular 2 Aspect-Oriented Programming in JavaScript Flux in Depth. Store and Network Communication. Using JSX with TypeScript Flux in Depth. Overview and Components. Even Faster AngularJS Data Structures Boost the Performance of an AngularJS Application Using Immutable Data - Part 2 Angular2 - First Impressions Build Your own Simplified AngularJS in 200 Lines of JavaScript Persistent State of ReactJS Component Boost the Performance of an AngularJS Application Using Immutable Data Processing Binary Protocols with Client-Side JavaScript Stream your Desktop to HTML5 Video Element Multi-User Video Conference with WebRTC Asynchronous calls with ES6 generators Binary Tree iterator with ES6 generators WebRTC chat with React.js AngularJS in Patterns (Part 3) AngularJS in Patterns (Part 2). Services. Using GitHub Pages with Jekyll! AngularJS in Patterns (Part 1). Overview of AngularJS Singleton in JavaScript Express over HTTPS What I get from the JavaScript MV* frameworks Remote Desktop Client with AngularJS and Yeoman The magic of $resource (or simply a client-side Active Record) AngularJS Inheritance Patterns AngularAOP v0.1.0 Advanced JavaScript at Sofia University AngularJS style guide Lazy prefetching of AngularJS partials VNC client on 200 lines of JavaScript Aspect-Oriented Programming with AngularJS CSS3 flipping effect Practical programming with JavaScript Why I should use publish/subscribe in JavaScript JavaScript, the weird parts Functional programming with JavaScript plainvm Looking for performance? Probably you should NOT use [].sort (V8) JavaScript image scaling ELang Caching CSS with localStorage Self-invoking functions in JavaScript (or Immediately Invoked Function Expressions) Asus N56VZ + Ubuntu 12.04 (en) Asus N56VZ + Ubuntu 12.04 Debian Squeeze + LXDE on Google Nexus S (or having some fun while suffering) HTML5 image editor Курсови проекти – ФМИ Carousel Gallery SofiaJS...
Using Stripe with Angular (Deprecated)
2016-07-05 · via Minko Gechev's blog

The content in this post uses deprecated APIs

Stripe offer a set of APIs which allow businesses and individuals to easily accept and manage payments online. In a typical scenario you want your users to pay for a service that you’re offering using Stripe as a mediator.

Angular & Stripe

On the other hand, Angular is gathering more and more popularity. A lot of developers are including the framework as part of their production applications.

In this blog post we will briefly describe what problems you may face using the Stripe APIs with Angular and how you can solve them.

So lets begin!

Sample Application

App Demo

This app is based on the angular2-seed project. The complete demo is located here.

Above you can see two views:

  • Default Stripe Form.
  • Custom Stripe Form.

The first one demonstrates the default Stripe checkout mechanism and the second one gets the token for the entered credit card by using a custom form.

Stripe Checkout

Our end goal here is to add a button which triggers the default stripe checkout mechanism and visualizes a form for entering credit card information.

First, we need to add the checkout script which will expose the global variable StripeCheckout. Open index.html (notice do not add this script to any of your components’ templates because Angular will remove it), and add the following line:

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

As next step we need to add a button to our component’s template:

<h1>Default Stripe Form</h1>

<button (click)="openCheckout()">Purchase</button>

Notice that we also added a click handler to the button. Once the user clicks on the button, the openCheckout method defined in the component’s controller will be invoked.

Now lets take a look at the component’s controller implementation:

import { Component } from '@angular/core';

@Component({
  moduleId: module.id,
  selector: 'sd-stripe-form',
  templateUrl: 'stripe-form.component.html',
  styleUrls: ['stripe-form.component.css']
})
export class StripeFormComponent {

  openCheckout() {
    var handler = (<any>window).StripeCheckout.configure({
      key: 'pk_test_oi0sKPJYLGjdvOXOM8tE8cMa',
      locale: 'auto',
      token: function (token: any) {
        // You can access the token ID with `token.id`.
        // Get the token ID to your server-side code for use.
      }
    });

    handler.open({
      name: 'Demo Site',
      description: '2 widgets',
      amount: 2000
    });

  }

}

Once the user clicks on the button and the openCheckout method is invoked, we create a new Stripe handle. To the handle we pass configuration object, and right after that we invoke the open method of the handle! This will open our stripe checkout pop-up.

So far so good! The token callback that we pass with the handle’s configuration object will return the id of the token associated with the user’s card. This token can be used, for instance, server-side. You may experience some issues using it because of the way stripe sends requests to their APIs.

So now lets go to the next section and see what the issues with the token callback are and how we can deal with them.

Custom Stripe Form

The first step of the custom form integration is to add the following script in your index.html file:

<script type="text/javascript" src="https://js.stripe.com/v2/"></script>

Right after that we need to set your Stripe public key:

<script type="text/javascript">
  Stripe.setPublishableKey('pk_test_oi0sKPJYLGjdvOXOM8tE8cMa');
</script>

As next step lets take a look at the custom Stripe form. It is encapsulated in the CustomFormComponent:

import { Component } from '@angular/core';
import { REACTIVE_FORM_DIRECTIVES } from '@angular/forms';

@Component({
  moduleId: module.id,
  selector: 'sd-custom-form',
  templateUrl: 'custom-form.component.html',
  styleUrls: ['custom-form.component.css'],
  directives: [REACTIVE_FORM_DIRECTIVES]
})
export class CustomFormComponent {...}

…with the following template:

<h1>Custom Stripe Form</h1>

<form action="" method="POST" id="payment-form" (submit)="getToken()">
  <span class="payment-message">{{message}}</span>

  <div class="form-row">
    <label>
      <span>Card Number</span>
      <input [(ngModel)]="cardNumber" name="card-number" type="text" size="20" data-stripe="number">
    </label>
  </div>

  <div class="form-row">
    <label>
      <span>Expiration (MM/YY)</span>
      <input [(ngModel)]="expiryMonth" name="expiry-month" type="text" size="2" data-stripe="exp_month">
    </label>
    <span> / </span>
    <input [(ngModel)]="expiryYear" name="expiry-year" type="text" size="2" data-stripe="exp_year">
  </div>

  <div class="form-row">
    <label>
      <span>CVC</span>
      <input [(ngModel)]="cvc" name="cvc" type="text" size="4" data-stripe="cvc">
    </label>
  </div>

  <input type="submit" value="Submit Payment">
</form>

Notice that in our form we bind to the properties cardNumber, expiryMonth, expiryYear, cvc. Once the user fills the form, she can click on the Submit Payment button and the method getToken() will be invoked. Notice that in order to use [(ngModel)] each input should have a name.

As next step, lets take a look at the CustomFormComponent class. The most straightforward implementation looks like:

import { Component } from '@angular/core';
import { REACTIVE_FORM_DIRECTIVES } from '@angular/forms';

@Component({
  moduleId: module.id,
  selector: 'sd-custom-form',
  templateUrl: 'custom-form.component.html',
  styleUrls: ['custom-form.component.css'],
  directives: [REACTIVE_FORM_DIRECTIVES]
})
export class CustomFormComponent {
  cardNumber: string;
  expiryMonth: string;
  expiryYear: string;
  cvc: string;

  message: string;

  getToken() {
    this.message = 'Loading...';

    (<any>window).Stripe.card.createToken({
      number: this.cardNumber,
      exp_month: this.expiryMonth,
      exp_year: this.expiryYear,
      cvc: this.cvc
    }, (status: number, response: any) => {
      if (status === 200) {
        this.message = `Success! Card token ${response.card.id}.`;
      } else {
        this.message = response.error.message;
      }
    });
  }
}

Change detection not performing

As can be seen from the video above, the form behaves weirdly. Once we submit the credit card only the Loading... label shows and after that we need to change value in the form in order to see the response gotten from Stripe, once the request completes.

What actually happens is that:

  • We send request to Stripe and respectively update the message to value Loading....
  • The request gets processed and we receive a response.
  • Once we change an input value, we trigger the change detection mechanism which renders the response on the screen.

But why the change detection doesn’t get triggered after we get the response?

In the Zones…

The Angular Zones are monkey patching (almost) all asynchronous APIs! Zones intercept async events and triggering the change detection on special events. Zones take care of XMLHttpRequests, fetch, setTimeout, etc. However, Stripe uses hidden iframe for establishing requests to its API, instead of the APIs that zone.js takes care of:

Hidden iframe

In order to workaround this issue, we need to run the callback in the context of the Angular Zone (or NgZone). In order to do this we need to inject NgZone in the CustomFormComponent and slightly change getToken:

@Component(...)
export class CustomFormComponent {

  // Same properties declaration

  constructor(private _zone: NgZone) {}

  getToken() {
    this.message = 'Loading...';

    (<any>window).Stripe.card.createToken({
      number: this.cardNumber,
      exp_month: this.expiryMonth,
      exp_year: this.expiryYear,
      cvc: this.cvc
    }, (status: number, response: any) => {

      // Wrapping inside the Angular zone
      this._zone.run(() => {
        if (status === 200) {
          this.message = `Success! Card token ${response.card.id}.`;
        } else {
          this.message = response.error.message;
        }
      });
    });
  }
}

Conclusion

In this short blog post we described how we can easily use Stripe with Angular. We went through the default Stripe checkout and after that we built a custom form!

The only tricky thing that we found in the first part of our experiment was the sanitization of the components’ templates by Angular. It prevents us from using:

<form action="/charge" method="POST">
  <script
    src="https://checkout.stripe.com/checkout.js" class="stripe-button"
    data-key="pk_test_oi0sKPJYLGjdvOXOM8tE8cMa"
    data-amount="2000"
    data-name="Demo Site"
    data-description="2 widgets"
    data-image="/img/documentation/checkout/marketplace.png"
    data-locale="auto">
  </script>
</form>

and instead makes to be a bit more creative :-).

With the custom form we faced another issue related to not triggering the Angular change detection once we get the card token. We discovered that this is due the uncommon way Stripe performs the Ajax request for registering the user’s card.

We solved the issue simply by wrapping the callback inside of a zone.run(() => {...}) call!