Я следил за этой документацией, чтобы реализовать Stripe Recurring Payments, которая отлично работает для обоих карты без 3D-Secure и карты с поддержкой 3D-Secure. Для первоначальных платежей я получаю от Stripe модальную аутентификацию 3d-Secure Payment для аутентификации платежей с использованием тестовых карт 3D-Secure.
Подписка создается вместе со счетами, которые Stripe генерирует для меня. Кажется, все в порядке, НО для 3D-Secure Enabled (Stripe Testing Cards) первый платеж проходит успешно, но последующие платежи, для которых я установил интервал в 1 день (для тестирования), Stripe "проваливает". Насколько я понимаю, Stripe снова требует аутентификации клиента для продолжения подписки.
В this говорится
При обнаружении 3D Secure настройте параметры выставления счетов, чтобы отправить размещенную ссылку вашему клиенту для завершения процесса.
Это совершенно нормально, но почему они даже снова требуют шаг аутентификации, если первый платеж прошел успешно и подписка запущена? Я просмотрел все связанные документы, но не нашел ничего, что могло бы предотвратить это.
Что делать, если Клиент не проверяет свою электронную почту, чтобы вернуться в мое Приложение для аутентификации платежей? ИЛИ
Что-то не так с кодом? Под этим я подразумеваю, что Stripe должен был сохранить способ оплаты, то есть номер карты, чтобы он не требовал аутентификации клиента при последующих платежах каждый раз, когда начинается новый платежный цикл (ежемесячно / раз в два года).
Я поискал в Интернете и нашел и ответил, что
Вы используете stripe.createToken, который не поддерживает аутентификацию 3ds. Вам необходимо перенести свой клиентский код на stripe.createPaymentMethod, который его поддерживает.
Это оно? Любой ответ приветствуется
Мой Payment.js
// set your stripe publishable key
var stripe = Stripe('pk_test_key');
var elements = stripe.elements();
var style = {
base: {
color: '#32325d',
lineHeight: '18px',
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '16px',
'::placeholder': {
color: '#aab7c4'
}
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
}
};
var cardNumber = elements.create('cardNumber', {
style: style
});
cardNumber.mount('#cardNumber');
var cardExpiry = elements.create('cardExpiry', {
style: style
});
cardExpiry.mount('#cardExpiry');
var cardCvc = elements.create('cardCvc', {
style: style
});
cardCvc.mount('#cardCVC');
var cardholderName = $('#custName').val();
var amount = $('#amount').val();
var cardButton = document.getElementById('makePayment');
cardButton.addEventListener('click', function(ev) {
// alert();
ev.preventDefault();
stripe.createToken(cardNumber).then(function(result) {
if (result.error) {
} else {
//$body.addClass("loading");
fetch('process.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
token_id: result.token.id
})
}).then(function(result) {
// Handle server response (see Step 3)
result.json().then(function(json) {
handleServerResponse(json);
//alert();
})
});
}
});
});
function handleServerResponse(response) {
if (response.error) {
// Show error from server on payment form
} else if (response.requires_action) {
// Use Stripe.js to handle required card action
var action = response.next_action;
if (action && action.type == 'redirect_to_url') {
window.location = action.redirect_to_url.url;
}
handleAction(response);
} else {
console.log("3D" + response);
}
}
function handleAction(response) {
var paymentIntentSecret = response.payment_intent_client_secret;
stripe.handleCardPayment(paymentIntentSecret).then(function(result) {
if (result.error) {
// Display error.message in your UI.
} else {
// The payment has succeeded. Display a success message.
alert('Payment succeeded!');
}
});
}
Process.php
<?php
require_once('stripe-php/init.php');
\Stripe\Stripe::setApiKey('sk_test_key');
$json_str = file_get_contents('php://input');
$json_obj = json_decode($json_str);
//print_r($json_obj->token_id);die;
//$intent = null;
try {
if (isset($json_obj->token_id)) {
//Create Customer
$customer = \Stripe\Customer::create([
"email" => "[email protected]",
"name" => "Haroon",
"source" => $json_obj->token_id,
]);
//create product
$product = \Stripe\Product::create([
'name' => 'Water',
'type' => 'service',
]);
//create a plan
$plan = \Stripe\Plan::create([
'product' => $product->id,
'nickname' => 'Water',
'interval' => 'day',
'currency' => 'eur',
'amount' => 1200.00,
]);
//add subscription to stripe
$subscription = \Stripe\Subscription::create([
'customer' => $customer->id,
'items' => [[
"plan" => $plan->id],],
'expand' => ['latest_invoice.payment_intent'],
]);
}
$intent = \Stripe\PaymentIntent::retrieve($subscription->latest_invoice->payment_intent->id);
$subscription = \Stripe\Subscription::retrieve($subscription->id);
// $intent->confirm();
// }
generatePaymentResponse($intent,$subscription);
}catch (\Stripe\Error\Base $e) {
# Display error on client
echo json_encode([
'error' => $e->getMessage()
]);
}
function generatePaymentResponse($intent,$subscription) {
if ($intent->status == 'requires_action' && $subscription->status == 'incomplete' &&
$intent->next_action->type == 'use_stripe_sdk' ) {
# Tell the client to handle the action
echo json_encode([
'requires_action' => true,
'payment_intent_client_secret' => $intent->client_secret
]);
} else if ($intent->status == 'succeeded' && $subscription->status == 'active') {
# The payment didn’t need any additional actions and completed!
# Handle post-payment fulfillment
echo json_encode([
'success' => true
]);
} else if ($intent->status == 'requires_payment_method' && $subscription->status == 'incomplete') {
echo "Subscription failed";
} else {
# Invalid status
http_response_code(500);
echo json_encode(['error' => 'Invalid PaymentIntent status']);
}
}