您已使用Google Pay,Apple Pay或浏览器中的预定义卡在一次单击中向网站支付了多长时间?我很少得到它。恰恰相反:每个新的在线商店都为我提供了另一种模式。而且,我必须每次都尽职地搜索我的卡,以便将其中的数据重新打印到网站上。第二天,我要在另一家商店中付款,然后重复此过程。这不是很方便。尤其是当您了解其他选择时:最近几年,使用Payment Request API标准可以轻松在现代浏览器中解决此问题。让我们了解为什么不使用它,并尝试简化它的工作。
你在说什么?
几乎所有现代浏览器都实现了Payment Request API标准。它允许您在浏览器中调用一个模式窗口,用户可以通过该窗口在几秒钟内付款。这就是使用浏览器中的普通卡在Chrome中的显示方式:
这就是-在Safari中通过Apple Pay进行指纹支付时:
不仅速度快,而且功能强大:该窗口可让您显示整个订单以及内部单个商品和服务的信息他,可让您澄清客户信息和交货细节。尽管提供的API的便利性颇有争议,但是在创建请求时,所有这些都是自定义的。如何在Angular中使用?
Angular不提供使用Payment Request API的抽象。从Angular框中使用它的最安全方法是从“依赖注入”机制获取Document,从中获取Window对象并使用window.PaymentRequest。import {DOCUMENT} from '@angular/common';
import {Inject, Injectable} from '@angular/core';
@Injectable()
export class PaymentService {
constructor(
@Inject(DOCUMENT)
private readonly documentRef: Document,
) {}
pay(
methodData: PaymentMethodData[],
details: PaymentDetailsInit,
options: PaymentOptions = {},
): Promise<PaymentResponse> {
if (
this.documentRef.defaultView === null ||
!('PaymentRequest' in this.documentRef.defaultView)
) {
return Promise.reject(new Error('PaymentRequest is not supported'));
}
const gateway = new PaymentRequest(methodData, details, options);
return gateway
.canMakePayment()
.then(canPay =>
canPay
? gateway.show()
: Promise.reject(
new Error('Payment Request cannot make the payment'),
),
);
}
}
如果直接使用“付款请求”,那么所有隐式依赖项的问题都会出现:测试代码变得更加困难,因为“付款请求”不存在,因此应用程序在SSR中爆炸。我们希望没有任何抽象的全局对象。我们可以从@ ng-web-apis / common中获取WINDOW令牌,以安全地从DI获取全局对象。现在添加一个新的PAYMENT_REQUEST_SUPPORT。使用它,您可以在使用前检查付款请求API的支持,现在,在不支持它的环境中,我们绝不会意外调用该API。export const PAYMENT_REQUEST_SUPPORT = new InjectionToken<boolean>(
'Is Payment Request Api supported?',
{
factory: () => !!inject(WINDOW).PaymentRequest,
},
);
export class PaymentRequestService {
constructor(
@Inject(PAYMENT_REQUEST_SUPPORT) private readonly supported: boolean,
...
) {}
request(...): Promise<PaymentResponse> {
if (!this.supported) {
return Promise.reject(
new Error('Payment Request is not supported in your browser'),
);
}
...
}
让我们以Angular风格编写
通过上述方法,我们可以非常安全地进行付款,但是可用性仍保持在裸API浏览器的同一级别:我们调用具有三个参数的方法,将大量数据收集在一起,并将它们转换为所需的格式以最终调用付款方法。但是在Angular的世界中,我们习惯于方便的抽象:依赖注入机制,服务,指令和流。让我们看一下一种声明式解决方案,该解决方案使使用Payment Request API更加便捷:
在本示例中,购物篮如下所示:<div waPayment [paymentTotal]="total">
<div
*ngFor="let cartItem of shippingCart"
waPaymentItem
[paymentLabel]="cartItem.label"
[paymentAmount]="cartItem.amount"
>
{{ cartItem.label }} ({{ cartItem.amount.value }} {{ cartItem.amount.currency }})
</div>
<b>Total:</b> {{ totalSum }} ₽
<button
[disabled]="shippingCart.length === 0"
(waPaymentSubmit)="onPayment($event)"
(waPaymentError)="onPaymentError($event)"
>
Buy
</button>
</div>
一切都归功于三个指令:因此,我们获得了一个简单方便的界面来打开付款并处理其结果。它可以根据Angular Way的所有规范工作。指令本身以非常简单的方式连接:- 付款指令使用ContentChildren收集自身内部的所有商品,并实现PaymentDetailsInit-使用Payment Request API时必需的参数之一。
@Directive({
selector: '[waPayment][paymentTotal]',
})
export class PaymentDirective implements PaymentDetailsInit {
...
@ContentChildren(PaymentItemDirective)
set paymentItems(items: QueryList<PaymentItem>) {
this.displayItems = items.toArray();
}
displayItems?: PaymentItem[];
}
- 输出指令可跟踪按钮上的单击并发出最终的付款结果,该输出指令将从Dependency Injection树中拉出付款指令,以及从DI令牌设置的付款方式和其他选项。
@Directive({
selector: '[waPaymentSubmit]',
})
export class PaymentSubmitDirective {
@Output()
waPaymentSubmit: Observable<PaymentResponse>;
@Output()
waPaymentError: Observable<Error | DOMException>;
constructor(
@Inject(PaymentDirective) paymentHost: PaymentDetailsInit,
@Inject(PaymentRequestService) paymentRequest: PaymentRequestService,
@Inject(ElementRef) {nativeElement}: ElementRef,
@Inject(PAYMENT_METHODS) methods: PaymentMethodData[],
@Inject(PAYMENT_OPTIONS) options: PaymentOptions,
) {
const requests$ = fromEvent(nativeElement, 'click').pipe(
switchMap(() =>
from(paymentRequest.request({...paymentHost}, methods, options)).pipe(
catchError(error => of(error)),
),
),
share(),
);
this.waPaymentSubmit = requests$.pipe(filter(response => !isError(response)));
this.waPaymentError = requests$.pipe(filter(isError));
}
}
交钥匙解决方案
我们已经收集并实现了@ ng-web-apis / payment-request库中描述的所有想法:这是一个交钥匙的解决方案,使您可以通过服务和上述格式的指令安全,快速地使用Payment Request API。我们已经从@ ng-web-apis发布并支持了该库,这是一个开放源代码组,专门致力于为声明性样式的本地Web API实现轻量级Angular包装器。我们网站上还有其他一些API实现,它们不是Angular开箱即用的,但可能会让您感兴趣。