#javascript #php #json #stripe-payments
#javascript #php #json #stripe-платежи
Вопрос:
ОБНОВЛЕНО Я создал страницу ценообразования, которая использует Stripe Checkout для использования как кнопки одноразовой оплаты для продукта 1, так и кнопки оплаты подписки для продукта 2.
Моя цель — перенаправить кнопку одноразовой оплаты на Stripe Checkout с единовременным платежом и отдельно перенаправить платеж по подписке на проверку с повторяющимся платежом.
Согласно STRIPE, это можно сделать, используя подписку в качестве режима в CheckoutSession в create-checkout-session.php (примерный проект) :
Режим сеанса оформления заказа. Требуется при использовании цен или режима настройки. Пропустить подписку, если сеанс оформления заказа включает хотя бы один повторяющийся элемент.
В отличие от документов Stripe, следующая строка кода: 'mode' => 'subscription',
активирует ТОЛЬКО платежи по подписке, но не перенаправляет одноразовые платежи. Чтобы одноразовые платежи работали, я должен изменить его на: 'mode' => 'payment',
но тогда платежи по подписке не работают.
Вот код php, о котором идет речь:
<?php
require_once 'shared.php';
$domain_url = $config['domain'];
// Create new Checkout Session for the order
// Other optional params include:
// [billing_address_collection] - to display billing address details on the page
// [customer] - if you have an existing Stripe Customer ID
// [payment_intent_data] - lets capture the payment later
// [customer_email] - lets you prefill the email input in the form
// For full details see https://stripe.com/docs/api/checkout/sessions/create
// ?session_id={CHECKOUT_SESSION_ID} means the redirect will have the session ID set as a query param
$checkout_session = StripeCheckoutSession::create([
'success_url' => $domain_url . '/success.html?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => $domain_url . '/canceled.html',
'payment_method_types' => ['card'],
'mode' => 'subscription',
'line_items' => [[
'price' => $body->priceId,
'quantity' => 1,
]]
]);
echo json_encode(['sessionId' => $checkout_session['id']]);
И вот код javascript:
// Create a Checkout Session with the selected plan ID
var createCheckoutSession = function(priceId) {
return fetch("./create-checkout-session.php", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
priceId: priceId
})
}).then(function(result) {
return result.json();
});
};
// Handle any errors returned from Checkout
var handleResult = function(result) {
if (result.error) {
var displayError = document.getElementById("error-message");
displayError.textContent = result.error.message;
}
};
/* Get your Stripe publishable key to initialize Stripe.js */
fetch("./config.php")
.then(function(result) {
return result.json();
})
.then(function(json) {
var publishableKey = json.publishableKey;
var subscriptionPriceId = json.subscriptionPrice;
var onetimePriceId = json.onetimePrice;
var stripe = Stripe(publishableKey);
// Setup event handler to create a Checkout Session when button is clicked
document
.getElementById("subscription-btn")
.addEventListener("click", function(evt) {
createCheckoutSession(subscriptionPriceId).then(function(data) {
// Call Stripe.js method to redirect to the new Checkout page
stripe
.redirectToCheckout({
sessionId: data.sessionId
})
.then(handleResult);
});
});
// Setup event handler to create a Checkout Session when button is clicked
document
.getElementById("onetime-btn")
.addEventListener("click", function(evt) {
createCheckoutSession(onetimePriceId).then(function(data) {
// Call Stripe.js method to redirect to the new Checkout page
stripe
.redirectToCheckout({
sessionId: data.sessionId
})
.then(handleResult);
});
});
});
Возможно ли вообще иметь как разовые платежи, так и повторяющиеся платежи на одной странице с Stripe Checkout? Как я могу это сделать?
Обновление в соответствии с Bemn:
$checkout_session = StripeCheckoutSession::create([
'success_url' => $domain_url . '/success.html?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => $domain_url . '/canceled.html',
'payment_method_types' => ['card'],
'mode' => $body->mode
'line_items' => [[
'price' => $body->price_xxx,
// For metered billing, do not pass quantity
'quantity' => 1,
]],
'line_items' => [[
'price' => $body->price_zzz,
// For metered billing, do not pass quantity
'quantity' => 1,
]]
]);
echo json_encode(['sessionId' => $checkout_session['id']]);
И JS:
// Create a Checkout Session with the selected plan ID
var createCheckoutSession = function(priceId, mode) {
return fetch("./create-checkout-session.php", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
priceId: priceId,
mode: mode // <-- passing the mode, e.g. 'payment' or 'subscription'
})
}).then(function(result) {
return result.json();
});
};
И HTML:
<div data-stripe-priceid="pricexxx" data-stripe-mode="payment" id="onetime-btn" class="bold mt-2 d-inline-block w-100-after-md max-width-xxs py-2 btn btn-secondary">Ore Time</div>
<div data-stripe-priceid="pricexxx" data-stripe-mode="subscription" id="subscription-btn" class="bold mt-2 d-inline-block w-100-after-md max-width-xxs py-2 btn btn-secondary">Ore Time</div>
Комментарии:
1. Да, не уверен в документации, но я видел это много раз, когда на разных платформах присутствуют оба варианта. 🙂
2. Как вы реализуете это решение? Я пытался решить это на checkout-sessions.php досье. Вы знаете лучший способ?
3. Я помню, как создавал «продукт» на панели инструментов Stripe, а затем в коде создавал «планы» (т. Е. Планы платежей) «на лету» в зависимости от суммы подписки. В бэкэнде я использовал Stripe API следующим образом:
$new_plan = StripePlan::create([ 'id' => $amount, 'product' => $product_id, 'nickname' => $amount / 100 . ' GBP per month', 'interval' => 'month', 'currency' => 'gbp', 'amount' => $amount, ]);
откуда$product_id
берется из панели инструментов Stripe. Это было около 18 месяцев назад, хотя я не уверен, насколько это актуально.4. Затем я создал новую подписку, например,
$subscription = StripeSubscription::create([ 'customer' => $customer->id, 'items' => [['plan' => $amount]], ]);
(примечание. Я назвал планы в соответствии с суммой, чтобы я мог легко проверить, существует ли план на определенную сумму). Не уверен, насколько это полезно для вас, похоже, вы используете Stripe по-другому, чем я. Однако я подозреваю, что вам нужно посмотреть «продукты» и «планы».
Ответ №1:
Возможно ли вообще иметь как разовые платежи, так и повторяющиеся платежи на одной странице с Stripe Checkout?
Да. Ключ в том, что вы должны передать правильные параметры для генерации соответствующего идентификатора сеанса Stripe Checkout.
Как я могу это сделать?
- Серверная часть: есть функция для приема идентификатора цены Stripe и режима оплаты в качестве входных данных и возврата идентификатора сеанса Stripe Checkout в качестве выходных данных.
- Интерфейс: передать информацию о режиме оплаты
/create-checkout-session.php
. (см. Примечание, если вы не можете этого сделать)
Подробные сведения
Следующее решение предполагает, что:
- Вы генерируете идентификатор сеанса Stripe Checkout в серверной части. Затем этот идентификатор будет передан
.createCheckoutSession()
в интерфейсе js. - У вас есть одноразовый продукт (назовем его PAY) и периодическая подписка (назовем ее подпиской).
Интерфейс
Я думаю, вы близки. Что вам нужно сделать, так это передать mode
информацию в конечную точку вашего API:
// Create a Checkout Session with the selected plan ID
var createCheckoutSession = function(priceId, mode) { // <-- add a mode parameter
return fetch("./create-checkout-session.php", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
priceId: priceId,
mode: mode // <-- passing the mode, e.g. 'payment' or 'subscription'
})
}).then(function(result) {
return result.json();
});
};
Если это так, каждая кнопка оформления заказа на странице должна содержать соответствующую информацию о priceId и режиме оплаты. Вы можете сделать это, сохранив их с помощью атрибута data:
<div data-stripe-priceid="price_yyy" data-stripe-mode="subscription">Recurrent</div>
<div data-stripe-priceid="price_zzz" data-stripe-mode="payment">1-time</div>
Если это так, вы можете получить атрибуты данных, например click
, по событию.
Примечание: Если вы не можете добавить дополнительный параметр для указания режима, вам нужно определить, является ли данный идентификатор цены одноразовым или повторяющимся продуктом в серверной части. См. https://stripe.com/docs/api/prices/object?lang=php#price_object-type для получения более подробной информации.
Серверная часть
Вот 2 примера фрагментов кода из документации Stripe. Прямое копирование их не работает.
Ссылка для ОПЛАТЫ: https://stripe.com/docs/checkout/integration-builder
$checkout_session = StripeCheckoutSession::create([
'payment_method_types' => ['card'],
'line_items' => [[
'price_data' => [
'currency' => 'usd',
'unit_amount' => 2000,
'product_data' => [
'name' => 'Stubborn Attachments',
'images' => ["https://i.imgur.com/EHyR2nP.png"],
],
],
'quantity' => 1,
]],
'mode' => 'payment',
'success_url' => $YOUR_DOMAIN . '/success.html',
'cancel_url' => $YOUR_DOMAIN . '/cancel.html',
]);
В вашем случае вам может не понадобиться определять 'price_data'
. Вместо этого вы должны использовать 'price'
, как в следующем примере.
Ссылка на подраздел: https://stripe.com/docs/billing/subscriptions/checkout#create-session
$checkout_session = StripeCheckoutSession::create([
'success_url' => 'https://example.com/success.html?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => 'https://example.com/canceled.html',
'payment_method_types' => ['card'],
'mode' => 'subscription',
'line_items' => [[
'price' => $body->priceId,
// For metered billing, do not pass quantity
'quantity' => 1,
]],
]);
- Взгляните на эту ссылку: https://stripe.com/docs/api/checkout/sessions/create . Например
line_items
, вы можете просто использовать'price'
и передавать идентификатор цены (напримерprice_xxx
), что означает, что ваша'line_items'
воля выглядит следующим образом:
'line_items' => [[
'price' => $body->priceId,
'quantity' => 1,
]],
Для 'mode'
этого используйте значение из вашего запроса API. Это должно быть что-то вроде:
'mode' => $body->mode
Что означает, что в вашем бэкэнде вам лучше определить функцию (например generate_checkout_session
) для:
- проанализируйте тело json, полученное в запросе API
- получить
priceId
иmode
из проанализированных данных - используйте
priceId
иmode
вStripeCheckoutSession::create
и - возвращает идентификатор
checkout_session
Надеюсь, это (и ссылочные URL-адреса) могут вам помочь.
Комментарии:
1. Вероятно, я где-то что-то упускаю, но мне интересно, как это работает, если клиент покупает смесь товаров, требующих разовых платежей, и товаров, оплаченных с помощью подписки в одной транзакции -?
2. @wkille Боюсь, вы не можете этого сделать (по крайней мере, вы не можете сгенерировать сеанс Stripe Checkout, смешивающий два вида элементов).
3. Я думаю, что я правильно следил, пока вы не дошли до этой части: «Это означает, что в вашем бэкэнде вам лучше определить функцию, принимающую priceId и mode из вашего запроса API и возвращающую идентификатор checkout_session».
4. Я добавил то, что получил от вашего решения, оно по-прежнему не работает. Я что-то упустил?
5. @ChosenJuan ваш серверный
create-checkout-session.php
файл получит строку JSON из интерфейса.$checkout_session = StripeCheckoutSession::create([ ...
не знает, чтоpriceId
иmode
автоматически, поэтому вам нужно проанализировать строку JSON, присвоитьpriceId
переменным php значения и,mode
полученные из интерфейса, и передать эти переменные в функцию сеанса create Stripe в ответе @Bemn вместоpriceId
andmode
. См. geeksforgeeks.org/how-to-receive-json-post-with-php
Ответ №2:
При создании сеанса вы можете указать как цену за повторяющуюся сумму, взимаемую за подписку, так и другую цену за разовую плату, которую вы хотите взимать. Вы можете комбинировать несколько повторяющихся цен и единовременные цены в целом.
$checkout_session = StripeCheckoutSession::create([
'success_url' => $domain_url . '/success.html?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => $domain_url . '/canceled.html',
'payment_method_types' => ['card'],
'mode' => 'subscription',
'line_items' => [
// Add a one-time Price for $10
[
'price' => 'price_123',
'quantity' => 1,
],
// Add another one-time Price for $23
[
'price' => 'price_345',
'quantity' => 1,
],
// Add a recurring Price for $100 monthly
[
'price' => 'price_ABC',
'quantity' => 1,
],
]);
Приведенный выше код создаст сеанс с 3 позициями. Один за 100 долларов в месяц, один за 10 долларов только один раз и один за 23 доллара только один раз. Общая сумма за сеанс составит 133 доллара при первом платеже. Он также запустит подписку на 100 долларов в месяц, а будущие счета-фактуры будут стоить 100 долларов, как и ожидалось.
Комментарии:
1. Это просто суммирует все цены вместе. Я хотел иметь одну кнопку для запуска единовременных платежей и другую кнопку для запуска ежемесячных платежей.
2. @ChosenJuan Ах, извините, я неправильно понял вопрос. Идея здесь заключалась бы в том, чтобы в вашей форме было две кнопки, которые вызывают ваш сервер, который создает сеанс оформления заказа с правильными позициями на основе того, что именно покупает клиент.
3. Да, точно, у меня будут две кнопки, которые перенаправляют либо на план подписки, либо на единовременную плату. Вы знаете, как решить эту проблему?
4. @ChosenJuan Я не уверен, какая именно часть вас блокирует. Ваш серверный код должен передавать правильные параметры для создания сеанса в зависимости от того, хотите ли вы одноразовый платеж или подписку. Это означает передачу вправо
line_items
и вправоmode
. По этой причине ваш клиентский код JS отправит правильную информацию на сервер. После создания сеанса вы можете перенаправить его на сторону клиента.5. Проблема не в элементах line_items, проблема в том, что режим может перенаправлять только на платежи по подписке или единовременные платежи, но не на оба. Как я могу изменить режим, чтобы принимать оба? Или вы знаете, как исправить эту проблему в формате Json или JS?
Ответ №3:
Что я получил, так это то, что вам просто нужно добавить проверку на то, является ли она одноразовой или подпиской, вот что вы можете сделать:
JS FILE CHANGES:
var createCheckoutSession = function(priceId, $mode) {
return fetch("./create-checkout-session.php", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
priceId: priceId,
paymentType: $mode, // This vary based on the button clicked either one-time or subscription.
})
}).then(function(result) {
return result.json();
});
};
/* Get your Stripe publishable key to initialize Stripe.js */
fetch("./config.php")
.then(function(result) {
return result.json();
})
.then(function(json) {
var publishableKey = json.publishableKey;
var subscriptionPriceId = json.subscriptionPrice;
var onetimePriceId = json.onetimePrice;
var stripe = Stripe(publishableKey);
// Setup event handler to create a Checkout Session when button is clicked
document
.getElementById("subscription-btn")
.addEventListener("click", function(evt) {
createCheckoutSession(subscriptionPriceId, 'subscription').then(function(data) {
// Call Stripe.js method to redirect to the new Checkout page
stripe
.redirectToCheckout({
sessionId: data.sessionId
})
.then(handleResult);
});
});
// Setup event handler to create a Checkout Session when button is clicked
document
.getElementById("onetime-btn")
.addEventListener("click", function(evt) {
createCheckoutSession(onetimePriceId, 'onetime').then(function(data) {
// Call Stripe.js method to redirect to the new Checkout page
stripe
.redirectToCheckout({
sessionId: data.sessionId
})
.then(handleResult);
});
});
});
Теперь нам нужно внести изменения в PHP-файл:
PHP FILE CHANGES:
$checkout_session = StripeCheckoutSession::create([
'success_url' => $domain_url . '/success.html?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => $domain_url . '/canceled.html',
'payment_method_types' => ['card'],
'mode' => $body->paymentType, // Here is what we have got from front-end
'line_items' => [[
'price' => $body->priceId,
'quantity' => 1,
]]
]);