在Angular中使用ngx-stripe将Stripe的支付表单嵌入到应用程序中的方法

本文是Angular Advent Calendar 2022 第18天的文章。

如果您在Angular中开发Web服务或应用程序,可以使用方便的库”ngx-stripe”来集成Stripe。

 

本文介绍了在Angular项目中使用ngx-stripe集成付款表单的方法。

关于Angular版本

    • “@angular/core”: “^15.0.0”

 

    • “ngx-stripe”: “^14.1.0”

 

    “@stripe/stripe-js”: “^1.44.1”

Angular应用的设置

首先要设置应用程序。

$ npm i -g @angular/cli
$ ng new ng-stripe-demo

当您首次运行ng命令时,会询问您是否要设置自动补全。

? Would you like to enable autocompletion? This will set up your terminal so pressing TAB while typing Angular CLI commands will show possible options 
and autocomplete arguments. (Enabling autocompletion will modify configuration files in your home directory.) (Y/n) 

除非使用共享机器或者将来没有计划使用Angular,否则建议使用y来进行设置。

为了使本次演示简单明了,省略了路由和CSS的元语言。

? Would you like to add Angular routing? No
? Which stylesheet format would you like to use? CSS

一旦设置完成,将会显示以下消息。

✔ Packages installed successfully.
    Successfully initialized git.

让我们进入项目目录并启动开发服务器。

$ ng serve
Build at: 2022-11-30T08:43:42.491Z - Hash: 04ce66ad26425dbf - Time: 5930ms

** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **


✔ Compiled successfully.

访问显示的URL(http://localhost:4200/),确保演示界面正常显示。

スクリーンショット 2022-11-30 17.47.12.png

使用ngx-stripe将Stripe的支付表单集成进来。

从这里开始使用ngx-stripe,将支付表单集成进来。

 

图书馆的安装和设置

请按照文档的指示安装库。

$ npm install ngx-stripe @stripe/stripe-js
ngx-stripe的14.1.0版本之前,在安装Angular v15时可能会出现错误。
根据GitHub的问题报告,它指出“由于内部变更需要更多时间来发布。”
相关链接:https://github.com/richnologies/ngx-stripe/issues/187
目前,您可以通过npm install ngx-stripe –force进行安装,如果安装不成功,请尝试使用此方法。

让我们在NgModule中加载已安装的ngx-stripe。

将 src/app/app.module.ts 文件修改如下。

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
+import { NgxStripeModule } from 'ngx-stripe';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
+    NgxStripeModule.forRoot('pk_test_から始まるStripeの公開可能APIキー')
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

调用API来获取Payment Intent。

在显示Stripe.js的卡片元素之前,需要预先创建Payment Intent或Setup Intent(仅保存卡片信息等情况)。

在Stripe文档的快速入门中准备API。

 

在取得的付款意图中显示支付表单。

通过调用准备好的API,例如快速入门,展示支付表单。

由于要使用HttpClientModule,所以需要先在src/app/app.module.ts中进行设置。


import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
+import { HttpClientModule } from '@angular/common/http';
import { NgxStripeModule } from 'ngx-stripe';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
+    HttpClientModule,
    NgxStripeModule.forRoot('pk_test_から始まるStripeの公開可能APIキー')
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

将src/app/app.component.ts进行如下更改。

-import { Component } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Observable } from 'rxjs';

type PaymentIntentAPIResponse = {
  clientSecret: string;
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

  clientSecret: string = '';

  constructor(
    private readonly http: HttpClient,
  ) {}

  ngOnInit() {
    this.createPaymentIntent()
      .subscribe(pi => {
        this.clientSecret = pi.clientSecret
      })
  }

  private createPaymentIntent(): Observable<PaymentIntentAPIResponse> {
    return this.http.post<PaymentIntentAPIResponse>(
      `http://localhost:3000/create-payment-intent`,
      {}
    );
  }
}

然后,用以下代码覆盖 src/app/app.component.html 文件。


<form>
  <ng-container *ngIf="clientSecret">
    <ngx-stripe-payment [clientSecret]="clientSecret">
    </ngx-stripe-payment>
    <button>支払う</button>
  </ng-container>
</form>

保存两个文件后,应用界面将变为结算表单。

スクリーンショット 2022-11-30 18.59.17.png

让我们来实现表单的提交处理

最后,我们需要添加输入信用卡信息后的”提交”处理。

由于我们将使用FormsModule,所以需要先在src/app/app.module.ts文件中进行设置。


import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { NgxStripeModule } from 'ngx-stripe';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
+    FormsModule,
    HttpClientModule,
    NgxStripeModule.forRoot('pk_test_から始まるStripeの公開可能APIキー')
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

让我们在src/app/app.component.ts文件中设置一个将函数设置为form的选项。

-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, ViewChild } from '@angular/core';
import { HttpClient } from '@angular/common/http';
+import { StripePaymentElementComponent, StripeService } from 'ngx-stripe';
import { Observable } from 'rxjs';

type PaymentIntentAPIResponse = {
  clientSecret: string
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
+  @ViewChild(StripePaymentElementComponent) paymentElement?: StripePaymentElementComponent

  clientSecret: string = '';

  constructor(
+    private readonly stripeService: StripeService,
    private readonly http: HttpClient,
  ) {}

  ngOnInit() {
    this.createPaymentIntent()
      .subscribe(pi => {
        this.clientSecret = pi.clientSecret
      });
  }

  private createPaymentIntent(): Observable<PaymentIntentAPIResponse> {
    return this.http.post<PaymentIntentAPIResponse>(
      `http://localhost:3000/create-payment-intent`,
      {}
    );
  }

+  pay() {
+    if (!this.paymentElement) return;
+    this.stripeService.confirmPayment({
+      elements: this.paymentElement.elements,
+      redirect: 'if_required',
+    }).subscribe(result => {
+      console.log('Result', result);
+      if (result.error) {
+        alert( 'Failed' );
+      } else {
+        if (result.paymentIntent?.status === 'succeeded') {
+          alert( 'success' );
+        }
+      }
+    });
+  }
}

我們利用ViewChild,在ngx-stripe提供的Payment Element組件中獲取存取權限。

随后,正在通过ngx-stripe的StripeService提供的Stripe.js方法confirmPayment执行。

将此pay方法与form的submit事件关联起来。

-<form>
+<form (ngSubmit)="pay()">
  <ng-container *ngIf="clientSecret">
    <ngx-stripe-payment [clientSecret]="clientSecret">
    </ngx-stripe-payment>
    <button type="submit">支払う</button>
  </ng-container>
</form>

我们可以使用Stripe提供的“测试卡号”来进行支付测试。

 

只要成功出现,就表示实施已完成。

スクリーンショット 2022-11-30 19.49.22.png

如果想要创建订阅申请表格。

现在我们也可以利用这次的实施来填写订阅申请表格。

让我们将创建Payment Intent的处理部分更改为以下代码。

    const subscription = await stripe.subscriptions.create({
        customer: customer.id,
        items: [{
            price_data: {
                unit_amount: 1000,
                currency: 'jpy',
                recurring: {
                    interval: 'month',
                },
                product: product.id
            }
        }],
        payment_behavior: 'default_incomplete',
        expand: ['latest_invoice.payment_intent'],
        payment_settings: {
            save_default_payment_method: 'on_subscription',
        },
    })

    return {
        clientSecret: ((subscription.latest_invoice as Stripe.Invoice).payment_intent as Stripe.PaymentIntent).client_secret
    }

即使在Stripe的订阅中,首次支付金额也会在内部生成支付意图。

因此,您可以获取创建的Payment Intent的clientSecret,并将其返回给前端,从而实现在Angular侧处理订阅申请,而无需更改表单的实现方式。

此外,在实际的嵌入式系统中,需要满足特定的商业交易法等要求。

请参考消费者机构的PDF等文件,一起进行织入除表单外的组件。

 

bannerAds