التسوق عبر الإنترنت مع API طلب الدفع و Angular

كم من الوقت دفعت على الموقع بنقرة واحدة باستخدام Google Pay أو Apple Pay أو بطاقة محددة مسبقًا في متصفحك؟

نادرا ما أحصل عليه.

بل على العكس تمامًا: يقدم لي كل متجر جديد عبر الإنترنت قالبًا آخر. ويجب علي في كل مرة البحث عن بطاقتي من أجل إعادة طبع البيانات منها إلى الموقع. في اليوم التالي ، أود أن أدفع مقابل شيء ما في متجر آخر وأكرر هذه العملية.

هذه ليست مريحة للغاية. خاصة عندما تعرف عن بديل: في العامين الماضيين ، يجعل معيار API طلب الدفع من السهل حل هذه المشكلة في المتصفحات الحديثة.

دعونا نفهم سبب عدم استخدامه ، ونحاول تبسيط العمل به.



ما الذي تتحدث عنه؟


تقوم جميع المتصفحات الحديثة تقريبًا بتطبيق معيار API لطلب الدفع . يسمح لك باستدعاء نافذة مشروطة في المتصفح يمكن للمستخدم من خلالها إجراء الدفع في غضون ثوان. هذه هي الطريقة التي يمكن أن تبدو بها في Chrome باستخدام بطاقة عادية من المتصفح:



وهنا - في Safari عند الدفع ببصمة الإصبع عبر Apple Pay:



فهي ليست سريعة فحسب ، ولكنها تعمل أيضًا: تتيح لك النافذة عرض معلومات حول الطلب بأكمله وعن السلع والخدمات الفردية داخل تسمح له بتوضيح معلومات العميل وتفاصيل التسليم. يتم تخصيص كل هذا عند إنشاء الطلب ، على الرغم من أن راحة واجهة برمجة التطبيقات المقدمة أمر مثير للجدل إلى حد ما.

كيفية استخدامها في الزاوي؟


لا تقدم Angular ملخصات لاستخدام واجهة برمجة تطبيقات طلب الدفع. الطريقة الأكثر أمانًا لاستخدامه من الصندوق في Angular هي الحصول على المستند من آلية حقن التبعية ، والحصول على كائن النافذة منه والعمل مع النافذة.

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 لأن طلب الدفع غير موجود. نأمل لكائن عالمي دون أي تجريد.

يمكننا أخذ رمز WINDOW من @ ng-web-apis / الشائعة للحصول على الكائن العمومي من DI بأمان. أضف الآن PAYMENT_REQUEST_SUPPORT جديد . سيسمح لك بالتحقق من دعم واجهة برمجة تطبيقات طلب الدفع قبل استخدامه ، ولن يكون لدينا الآن مكالمة عرضية لواجهة برمجة التطبيقات في بيئة لا تدعمها.

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'),
           );
       } 
      ...
   }

لنكتب بأسلوب زاوي


باستخدام الطريقة الموضحة أعلاه ، يمكننا العمل مع الدفعات بأمان تام ، ولكن لا تزال قابلية الاستخدام على نفس المستوى من متصفح واجهة برمجة التطبيقات (API) العاري: نستدعي طريقة بثلاث معلمات ، ونجمع الكثير من البيانات معًا ونجمعها بالتنسيق المطلوب للاتصال أخيرًا طريقة الدفع او السداد.

لكن في عالم Angular ، نحن معتادون على التجريد المريح: آلية حقن التبعية والخدمات والتوجيهات والجداول. لنلقِ نظرة على حل تعريفي يجعل استخدام واجهة برمجة تطبيقات طلب الدفع أسرع وأسهل:



في هذا المثال ، تكون السلة على النحو التالي:

<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 - وهي إحدى الوسيطات المطلوبة عند العمل مع واجهة برمجة تطبيقات طلب الدفع.

@Directive({
   selector: '[waPayment][paymentTotal]',
})
export class PaymentDirective implements PaymentDetailsInit {
   ...
   @ContentChildren(PaymentItemDirective)
   set paymentItems(items: QueryList<PaymentItem>) {
       this.displayItems = items.toArray();
   }
 
   displayItems?: PaymentItem[];
}

  • يسحب توجيه الإخراج ، الذي يتتبع النقرات على الزر وينبعث نتيجة الدفع النهائية ، توجيه الدفع من شجرة حقن التبعية ، بالإضافة إلى طرق الدفع والخيارات الإضافية التي يتم تعيينها بواسطة رموز 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 :


هذا حل جاهز يسمح لك بالعمل مع API Request Payment بأمان وبسرعة من خلال الخدمة ومن خلال التوجيهات بالتنسيق الموضح أعلاه.

لقد نشرنا ودعمنا هذه المكتبة من @ ng-web-apis ، وهي مجموعة مفتوحة المصدر متخصصة في تنفيذ الأغلفة الزاوية خفيفة الوزن لواجهات برمجة تطبيقات الويب الأصلية ، بشكل رئيسي بأسلوب تعريفي. هناك تطبيقات أخرى لواجهة برمجة التطبيقات على موقعنا لا يتم تسليمها خارج الصندوق في Angular ، ولكنها قد تكون ذات فائدة لك.

All Articles