ادغام پرداخت 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 هستند استفاده کنید.
برای شما آرزوی موفقیت می کنم!