برنامه نویسی

ادغام پرداخت Paysafe.js در React.js با 3DS (راهنمای گام به گام)

ادغام یک دروازه پرداخت باید ساده باشد ، اما کار با Paysafe.js چیزی نیست. اگر تاکنون سعی کرده اید آن را در پروژه خود پیاده سازی کنید ، احتمالاً ناامیدی را می دانید: حداقل منابع آنلاین ، مستندات پراکنده و جزئیات اجرای مبهم.

بر خلاف Stripe یا PayPal ، جایی که می توانید آموزش های بی شماری ، بحث در مورد انجمن و پاسخ های سرریز پشته را پیدا کنید ، Paysafe.js مانند یک بیابان اطلاعات است. اسناد رسمی آنها فقط ملزومات لخت را فراهم می کند و بسیاری از موارد لبه را ناشناخته می کند. حتی کارهای ساده – مانند رسیدگی به خطاهای ، سفارشی کردن UI یا اشکال زدایی در مورد خرابی های پرداخت – آزمایش ، خطا و شیرجه های عمیق در بلیط های پشتیبانی.

در این پست چالش هایی که توسعه دهندگان هنگام ادغام Paysafe.js ، چه چیزی باید مراقب باشند و برخی از راه حل هایی که ممکن است باعث صرفه جویی در وقت شما شود ، برجسته خواهد شد. اگر در حال حاضر در حال مبارزه با این ادغام هستید ، تنها نیستید!

شرح تصویر

مرحله 1.

قلاب سفارشی ایجاد کنید که با paysafe.js سروکار داشته باشد.

'use client';

import { useState, useEffect } from 'react';
import { PaysafeError, PaysafeOptions } from '@/types/paySafe/type';

export const usePaysafe = (apiKey: string, options: PaysafeOptions) => {
  const [instance, setInstance] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    const loadScript = async () => {
      if (typeof window === 'undefined') return;

      if (!document.getElementById('paysafe-sdk')) {
        const script = document.createElement('script');
        script.src="https://hosted.paysafe.com/js/v1/latest/paysafe.min.js";
        script.id = 'paysafe-sdk';
        script.async = true;
        script.onload = () => {
          console.log('Paysafe SDK loaded');
          initializePaysafe();
        };
        script.onerror = () => {
          console.error('Failed to load Paysafe SDK');
          setError({
            code: 'SDK_LOAD_FAILED',
            detailedMessage: 'Failed to load Paysafe SDK',
          });
        };
        document.body.appendChild(script);
      } else {
        console.log('Paysafe SDK already loaded');
        initializePaysafe();
      }
    };

    const initializePaysafe = () => {
      if (window.paysafe) {
        window.paysafe.fields.setup(apiKey, options, (paysafeInstance: any, setupError: any) => {
          if (setupError) {
            console.error('Paysafe Setup Error:', setupError.detailedMessage);
            setError(setupError);
          } else {
            console.log('Paysafe setup successful');
            setInstance(paysafeInstance);
          }
        });
      } else {
        console.error('Paysafe SDK is not available after loading');
        setError({ code: 'SDK_NOT_LOADED', detailedMessage: 'Paysafe SDK not loaded' });
      }
    };

    loadScript();
  }, [apiKey, options]);

  // Additional Methods
  const checkFieldIsEmpty = (field: string) => {
    if (!instance) return false;
    return instance.fields[field]?.isEmpty() ?? false;
  };

  const checkFieldIsValid = (field: string) => {
    if (!instance) return false;
    return instance.fields[field]?.isValid() ?? false;
  };

  const checkAllFieldsValid = () => {
    if (!instance) return false;
    return instance.areAllFieldsValid();
  };

  return { instance, error, checkFieldIsEmpty, checkFieldIsValid, checkAllFieldsValid };
};

حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

فکر می کنی تمام شد؟ ما تازه شروع شده ایم!

شرح تصویر

مرحله 2

اکنون وقت آن رسیده است که از قلاب خود استفاده کنیم و گزینه هایی را به آن اضافه کنیم.

import { useState, useEffect } from 'react';
import { useMutation } from '@tanstack/react-query';
import { PaysafeError, PaysafeOptions } from '@/types/paySafe/type';
import { usePaysafe } from '@/hooks/usePaySafe';
import Image from 'next/image';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import useCustomToast from '@/hooks/useCustomToast';
import { PaymentContactType } from '@/types/flight/type';
import { get3DSOptions } from '@/utils/get3DSOptions';

const API_KEY = 'your API key';
const PAYSAFE_ACCOUNT_ID = 'your account id';

type Props = {
  amount: number;
  currency?: string;
  serverPayload: any;
  validPaymentContacts: PaymentContactType | null;
};

const OPTIONS: PaysafeOptions = {
  environment: 'TEST',
  currency: 'GBP',
  accounts: {
    default: PAYSAFE_ACCOUNT_ID,
  },
  fields: {
    cardNumber: { selector: '#card-number', placeholder: 'Card number' },
    expiryDate: { selector: '#expiration-date', placeholder: 'Expiration date' },
    cvv: { selector: '#cvv', placeholder: 'CVV' },
  },
};

const PaySafeForm = ({ amount, validPaymentContacts, serverPayload, currency = 'GBP' }: Props) => {
  const [cardBrand, setCardBrand] = useState(null);
  const [isLoading, setIsLoading] = useState(false);
  const [cardHolderName, setCardHolderName] = useState('');

  const { showToast } = useCustomToast();

  const { instance, error: setupError, checkAllFieldsValid } = usePaysafe(API_KEY, OPTIONS);

  const tokenizeMutation = useMutation({
    mutationFn: () => {
      return new Promise((_, reject) => {
        if (!instance) {
          return reject({ code: 'NO_INSTANCE', detailedMessage: 'No Paysafe instance available' });
        }

        instance.tokenize(
          get3DSOptions(
            amount,
            currency,
            PAYSAFE_ACCOUNT_ID,
            cardHolderName,
            serverPayload.bookingUuid || '',
            validPaymentContacts,
          ),
          function (_: any, error: any, result: { token: string }) {
            if (result) {
              console.log(result.token);
            }

            if (error) {
              console.error('Tokenization error:', error);
              reject(error);
              setIsLoading(false);
            }
          },
        );
      });
    },

    onError: (error: any) => {
      setIsLoading(false);
      console.log(error.detailedMessage || 'Tokenization failed');
      alert(error.detailedMessage || 'Tokenization failed');
    },
  });

  const handlePay = async () => {
    setIsLoading(true);
    if (checkAllFieldsValid()) {
      tokenizeMutation.mutate();
    } else {
      setIsLoading(false);
      showToast({
        type: 'error',
        content:
          'Kindly ensure that the "Card Number," "Expiration Date," and "CVV" are provided and valid.',
      });
    }
  };

  useEffect(() => {
    if (instance) {
      instance.cardBrandRecognition(function (_, event) {
        const recognizedCardBrand: any = event.data.cardBrand;

        if (recognizedCardBrand) {
          setCardBrand(recognizedCardBrand);
        } else {
          setCardBrand(null);
        }
      });

      instance.unsupportedCardBrand(function (instance, event) {
        alert('The provided card brand is not supported. Please choose another card brand.');
        console.log(instance, event);
      });
    }
  }, [instance]);

  const handleCardHolderNameChange = (e: any) => {
    setCardHolderName(e.target.value);
  };

  if (setupError) {
    return (
      

Error: {setupError.detailedMessage}

); } return (
); }; export default PaySafeForm;
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

مرحله 3.

شما می توانید عملکرد یاور را که استفاده کردیم تشخیص دهید ، get3DSOptions

import { PaymentContactType } from '@/types/flight/type';

export const get3DSOptions = (
  amount: number,
  currency: string,
  accountId: number,
  cardHolderName: string,
  bookingsUUID: string,
  validPaymentContacts: PaymentContactType | null,
) => ({
  amount: Math.round(amount * 100),
  openAs: 'IFRAME', // Ensures the tokenization happens with 3D Secure in an iframe
  transactionType: 'PAYMENT',
  merchantRefNum: bookingsUUID,
  paymentType: 'CARD',
  customerDetails: {
    holderName: cardHolderName,
    profile: {
      firstName: validPaymentContacts?.fullName?.split(' ')[0],
      lastName: validPaymentContacts?.fullName?.split(' ')[1],
      email: validPaymentContacts?.email,
    },
  },
  threeDS: {
    amount: Math.round(amount * 100),
    currency,
    accountId,
    merchantRefNum: bookingsUUID,
    useThreeDSecureVersion2: true,
    authenticationPurpose: 'PAYMENT_TRANSACTION',
    challengeRequestIndicator: 'CHALLENGE_MANDATED',
    threeDsMethodCompletionIndicator: 'Y',
    deviceChannel: 'BROWSER',
    messageCategory: 'PAYMENT',
    electronicDelivery: {
      email: validPaymentContacts?.email,
      isElectronicDelivery: true,
    },
    priorAuthenticationData: {
      authenticationMethod: 'SMS_OTP',
    },
    requestorChallengePreference: 'NO_PREFERENCE',
    transactionIntent: 'GOODS_OR_SERVICE_PURCHASE',
    profile: {
      email: validPaymentContacts?.email,
      phone: validPaymentContacts?.phone,
      cellPhone: validPaymentContacts?.phone,
    },
    userAccountDetails: {
      priorThreeDSAuthentication: {
        data: 'Some up to 2048 bytes undefined data',
        method: 'ACS_CHALLENGE',
        time: '2014-01-26T10:32:28Z',
      },
    },
    browserDetails: {
      javaEnabled: true,
      language: 'en-US',
      colorDepth: 24,
      screenHeight: 1080,
      screenWidth: 1920,
      timeZoneOffset: -120,
      userAgent:
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
    },
  },
  vault: {
    holderName: cardHolderName,
  },
});

حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

تبریک می گویم! ما تمام شد

شرح تصویر

در زیر ، پیوندهای مفیدی را به من پیوست می کنم که به من کمک زیادی می کند.

PS در محیط آزمایش ، سعی کنید فقط از کارتهای آزمایشی که دارای فعال سازی 3DS هستند استفاده کنید.

برای شما آرزوی موفقیت می کنم!

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا