noteにEC-CUBE×Stripeで疑似サブスクリプションの記事を書きました。
その技術的な部分、Stripeの実装をここに記す!!
EC-CUBE側は結構なカスタムを行っているので、この部分は省きます。
Stirpeで利用した機能
・The Setup Intents API(docs)
・Set up future payments(docs)
・SetupIntents(docs API)
・PaymentMethods(docs API)
・PaymentIntents(docs API)
・Customers(docs API)
EC-CUBEに実装した大まかな流れ
1.決済フロー「ご注文手続き」でSetupIntentsの作成、payment_method_idを取得
2.決済フロー「checkout」でCustomer作成、PaymentMethodにCustomerをアタッチ
3.決済フロー「checkout」でCustomerId、PaymentMethodIdを保存
SetupIntents, PaymentMethods, Customers
SetupIntentsは顧客のカード情報の認証情報を設定して保存できます。
すぐに支払いをせずにPaymentIntentsを利用して後から支払いできます。
カード情報の収集とSetupIntentsの作成
SetupIntentsを作成し、戻ってきた情報からpayment_methodのIDをcheckout時に保存します。
下記はEC-CUBEの決済フロー(ご注文手続き)に実装しています。
EC-CUBEに関わる部分は邪魔なので削除しています。
<div class="ec-orderPayment">
<div class="ec-rectHeading">
<h2>クレジットカード情報</h2>
<div id="card-element" ></div>
<div id="card-errors" role="alert"></div>
</div>
</div>
<script src="https://js.stripe.com/v3/"></script>
<script>
const publicKey = '{{ publicKey }}';
let stripe = Stripe(publicKey,{betas: ['payment_intent_beta_3']});
let elements = stripe.elements();
// スタイルのカスタマイズ
let style = {
base: {
color: '#32325d',
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '16px',
'::placeholder': {
color: '#aab7c4'
}
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
}
};
// クレジットカード情報入力欄の構築
let card = elements.create('card', {style: style, hidePostalCode: true});
card.mount('#card-element');
// 入力変更時のリスナー
// バリデーションメッセージを表示する
card.addEventListener('change', function(event) {
let displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
});
// submit時のリスナー
let setupIntent;
let form = document.getElementById('form-id');//用意したFORMのIDを設定してください。
form.addEventListener('submit', async function(event) {
event.preventDefault();
if($.isEmptyObject(setupIntent)){
await getSetupIntent();
}
if(setupIntent !== undefined){
stripe.confirmCardSetup(setupIntent.client_secret, {
payment_method: {
card: card
}
}).then(function(result) {
if (result.error) {
stripeDisplayErrorMessage(result.error.message);
} else {
// The PaymentMethod was successfully set up
stripePaymentMethodHandler(result.setupIntent.payment_method);
}
});
}
});
function stripePaymentMethodHandler(payment_method) {
let hiddenInputToken = document.createElement('input');
hiddenInputToken.setAttribute('type', 'hidden');
hiddenInputToken.setAttribute('name', 'stripe_payment_method_id');
hiddenInputToken.setAttribute('value', payment_method);
form.appendChild(hiddenInputToken);
// Submit the form
form.submit();
}
let getSetupIntent = function() {
return fetch('{{ setupIntent作成PHPのURL }}', {
method: "post",
headers: {
"Content-Type": "application/json",
}
}).then(function(response) {
return response.json();
}).then(function(setupIntentObj) {
if (setupIntentObj.error) {
stripeDisplayErrorMessage(setupIntentObj.error.message);
} else {
setupIntent = setupIntentObj;
}
});
};
function stripeDisplayErrorMessage(message) {
let displayError = document.getElementById('card-errors');
displayError.textContent = message;
}
</script>
use Stripe\Stripe;
use Stripe\SetupIntent;
public function setup_intent()
{
Stripe::setApiKey('{{ secretKey }}');
$setupIntent = SetupIntent::create([
'payment_method_types' => ['card'],
]);
return $setupIntent;//jsonに変換してください。
}
Customerの作成とPaymentMethodにCustomerをアタッチ、ID保存
checkoutはEC-CUBEの決済プラグインを参考に作成しています。
https://github.com/EC-CUBE/sample-payment-plugin
use Stripe\Customer;
use Stripe\PaymentMethod;
use Stripe\Stripe;
public function checkout()
{
Stripe::setApiKey('{{ secretKey }}');
//Customer作成
$StripeCustomer = Customer::create([
'name' => 'name',
'email' => 'email',
]);
//CustomerをpaymentMethodへアタッチ
$payment_method = PaymentMethod::retrieve('{{ paymentMethodId }}');
$payment_method->attach(['customer' => $StripeCustomer->id]);
//それぞれのIDを保存(略)
}
PaymentIntents
CustomerId, paymentMethodIdを利用してPaymentIntentsで決済を行います。
この部分はEC-CUBEのcommandを作成してcronで定期的に実行しています。
use Stripe\Stripe;
use Stripe\StripeClient;
$stripe = new StripeClient('{{ secretKey }}');
$createPaymentIntent = $stripe->paymentIntents->create([
'amount' => 1000,
'currency' => 'jpy',
'customer' => {{ customer_id }},
'payment_method' => {{ payment_method_id }},
'off_session' => true,
'confirm' => true,
]);
以上!!