Apple Pay — регулярный платеж
Я пытаюсь использовать регулярные платежи через Apple Pay . Похоже, платежная сессия настроена правильно. Появится правильное всплывающее окно, указывающее на немедленную оплату и регулярный платеж, платеж будет успешно отправлен в NMI, а ответ отправлен обратно клиенту.
Внешний код:
function subscribe() {
var request = {
countryCode: 'US',
currencyCode: 'USD',
supportedNetworks: ['visa', 'masterCard', 'amex', 'discover'],
merchantCapabilities: ['supports3DS'],
total: { label: 'Budmember', amount: packages?.Package_Amount__c.toFixed(2).toString()},
recurringPaymentRequest: {
managementURL:"https://7d50e9ae5e20.ngrok.io/billing",
tokenNotificationURL:"https://webhook.site/8bb38a60-be08-47f5-82ba-2bdccc7ec4cf/",
paymentDescription: "VIP Membership",
regularBilling: {
label: {amount: packages?.Package_Amount__c.toFixed(2).toString(), type: "final"},
amount: packages?.Package_Amount__c.toFixed(2).toString(),
paymentTiming: "recurring",
recurringPaymentIntervalUnit: "month",
recurringPaymentStartDate: moment().format("YYYY-MM-DD"),
}},
}
//start apple Pay session
var session = new window.ApplePaySession(3, request);
session.begin()
session.onvalidatemerchant = async (event) => {
const res = await applePaySessionRequest(event.validationURL)
console.log(res.data)
session.completeMerchantValidation(res.data)
}
//submit payment to backend
session.onpaymentauthorized = async (event) => {
const dataPackage= {
onboardingStep: '7',
salesForceId: cognitoUser.salesForceId,
packageId: packages?.Id,
billingPackage: {
first_name: currentUser.firstName,
last_name: currentUser.lastName,
company: "",
address1: "",
address2: "",
city: "",
state: "",
zip: "",
country:"USA",
phone: "",
fax: "",
email: currentUser.email,
}
}
const res = await applePayInitialSub(event.payment, dataPackage )
if (res?.data?.success) {
//update token for new field in the token
setOnboardingStep("7");
localStorage.setItem("token", res.data.token || localStorage.getItem("token"));
notification("Membership Payment Received","success","Payment Success");
handleBillingAddress()
} else {
notification(res.data.msg || "Card Declined" , "danger" , "Error");
setLoading(false)
}
session.completePayment({status: res.data.applePayMsg})
}
}
Серверный код (сеанс Apple Pay):
async function applePaySession(req, res, next) {
const sessionURL = req.body.sessionURL
let response = {};
try {
function promisifyRequest(options) {
return new Promise((resolve, reject) => {
request(options, (error, response, body) => {
if (body) {
return resolve(res.send(body));
}
if (error) {
return reject(error);
}
});
});
}
const options = {
url: sessionURL,
agentOptions: {
pfx: fs.readFileSync("./merchant_id.p12"),
passphrase: "SECRET",
},
method: 'post',
body: {
merchantIdentifier: "merchant.com.some.app",
displayName: "NAME",
initiative: "web",
initiativeContext: "some.url" // Frontend url as registered
},
json: true,
};
response = await promisifyRequest(options);
console.log(response)
} catch (error) {
console.log(error)
}
return response;
}
Бэкенд-код (оплата и подписка):
async function appleSubProcessor(req, res, next) {
try {
const conn = sfConn();
const { cognitoUserId, accessToken, email } = req.user.data
const {billingPackage} = req.body
const {onboardingStep, salesForceId, packageId} = billingPackage
const member = await conn.sobject("Contact").find({"Email": email})
const response = await NMI.getSubscriptionById(member[0].Subscription_ID__c)
// PROCESSING CODE!!!!!
//encodes data object to buffer
const data = req.body.paymentObject.token.paymentData
var buf = Buffer.from(JSON.stringify(data))
//encodes buffer to hex string
const hexCode = buf.toString("hex")
//if subscription is present on NMI only charge
if(response?.nm_response?.subscription?.subscription_id) {
await applePaymentOnly(member, hexCode, billingPackage, email, res)
} else {
await applePayInitialSub(req, member, hexCode, res, cognitoUserId, accessToken, email, billingPackage)
}
} catch(err) {
console.log(err)
}
}
async function applePayInitialSub(req, member, paymentToken, res, cognitoUserId, accessToken, email, billingPackage) {
try {
const conn = sfConn()
// const { cognitoUserId, accessToken, email } = req.user.data
// const {billingPackage} = req.body
const {onboardingStep, salesForceId, packageId} = billingPackage
const subsc_data = await conn.sobject("Packages__c").find({"Id": packageId})
const dueNow = subsc_data[0].Package_Amount__c.toFixed(2).toString()
//Error handling - no package available
if(!subsc_data || !subsc_data.length){
console.log("Error")
Sentry.withScope((scope) => {
scope.setLevel("fatal");
Sentry.captureException("Internal Error, Unable to get subscription data");
})
return res.json({
success: false,
message:"Internal Error. Unable to get subscription data"
})
}
//sends request
const response = await NMI.applePayInitialSubscription(billingPackage.billingPackage, paymentToken, dueNow, email, billingPackage.billingPackage.email)
console.log(response)
onboarding logic ...
}
Что я пробовал до сих пор:
Пополнить первоначальный токен. NMI ответил: «НЕ ЧИТАТЬ». Это указывало на то, что Apple Pay блокирует транзакцию. На карте не было предупреждений о мошенничестве, и я смог зарядить ее новым токеном.
Установите интервал подписки с Apple Pay на «минуту» и предпримите попытку перезарядки токена через 60 секунд. Опять же "НЕ ЧИТАТЬ".
Я установил tokenNotificationURL в конечную точку на моем сервере. Я туннелировал на свой локальный хост через NGROK для https. Я смог позвонить в конечную точку через почтальона. Я следил за документами Apple Developer Docs.Но никаких звонков от Apple не поступало.
Я сделал шаг 3 с подпиской на webhook.site pro. Звонок почтальону удался, звонков от Apple нет.
Я искал ответы на StackOverflow и нашел этот пост для Stripe. Здесь описано, что (с Stripe SDK) paymentRequest поставляется с прослушивателем «paymentmethod», который должен возвращать paymentMethod.id. Я попытался использовать onpaymentmethodselected от AppleJS, чтобы получить доступ к идентификатору, но в ответ получил только paymentMethod: {type: "Credit"}:
session.onpaymentmethodselected = async (event) => {session.completePaymentMethodSelection({newTotal: { сумма: "49,00", метка:'VIP-членство'}}) }
Я расшифровал платежный токен на бэкенде. Я следовал этому руководству, чтобы создать файлы сертификата и ключа (.pem). Его пакет использует устаревшую зависимость, которая не работает в новых версиях Node. Я использовал этот пакет для расшифровки токена. Однако мне не удалось зарядить applicationPrimaryAccountNumber. AVS, похоже, не разрешен, поэтому предоставление почтового индекса также не приводит к успешной транзакции. Ниже приведен расшифрованный токен, а также ответ NMI:
Я искал на SO и на форумах разработчиков Apple, но ничего не смог с этим сделать. Я работаю над этой проблемой в течение последних 3 дней. Любая помощь приветствуется!