Stripeで従量制月次決済 その4【実装編2】

毎月、締日の翌日に利用した数量と期間に応じて決済するシステムを作ることになった。

これまでの記事はこちら

Stripeで従量制月次決済 その1【導入編】
Stripe導入の経緯とStripe Connectとはどんなものか、アカウントタイプについてをざっくりと解説。
Stripeで従量制月次決済 その2【準備編】
Stripe Connectの実装に必要な連結アカウントのID・シークレットキー・公開可能キーの取得方法を解説。
Stripeで従量制月次決済 その3【実装編1】
Stripe Connectの決済に必要な顧客作成や決済手段の設定等の実装についてを解説。

支払いタイプについて

Stripe Connectにはダイレクト支払い・ディスティネーション支払い・支払いと送金別方式の3種類の支払いタイプがあり、売上の分配方法や明細書での支払いの表示方法に違いがある。

公式ドキュメント:https://docs.stripe.com/connect/charges#types

ダイレクト支払い

顧客と連結アカウントが直接取引をする。
連結アカウントの売上金額の一部を手数料としてプラットフォームへ送金することができる。
Stripe手数料の支払い、返金処理、チャージバックは連結アカウントが行う。

Standardアカウントにおすすめ。
今回はダイレクト支払いを使用。

ディスティネーション支払い

顧客とプラットフォームが取引をする。
プラットフォームは売上金額の一部または全額を連結アカウントに送金を行う。
Stripe手数料の支払い、返金処理、チャージバックはプラットフォームが行う。

Expressアカウント・Customアカウントにおすすめ。

支払いと送金別方式

顧客とプラットフォームが取引をする。
プラットフォームは複数の連結アカウントに送金を行うことができる。
Stripe手数料の支払い、返金処理、チャージバックはプラットフォームが行う。

モール型ECサイトのように、複数の連結アカウントの商品をショッピングカートでまとめて決済を行うような場合に支払いと送金別方式を使用する。

PaymentIntentの作成

SetupIntentの作成が終わり、決済の準備が完了。
作成したCustomerとPaymentMethodを使用して、決済の処理を行う。

公式ドキュメント:https://docs.stripe.com/payments/finalize-payments-on-the-server?platform=web&type=setup&client=html#charge-saved-payment-method

PaymentIntentとは

PaymentIntentとは決済フローの支払いの作成から決済までのステータスを追跡できるオブジェクト。

実装

支払い方法はカードのみで、ひとつしか設定しない仕様にしているのでこの方法を採用しているが、支払い方法が複数ある場合は、顧客情報からデフォルトのPaymentMethod IDを取得。

公式APIリファレンス:https://docs.stripe.com/api/payment_methods/customer_list

サポートされている決済手段のタイプについてはこちら

\Stripe\Stripe::setApiKey("連結アカウントのシークレットキー");

$payment_method_list = \Stripe\Customer::allPaymentMethods(
	"Customer ID",
	[
		'type' => 'card'
	]);

請求金額、プラットフォームに送金する手数料、Customer ID、取得したPaymentMethod ID等を設定。
currencyは使用する通貨に応じて設定。日本円ならjpy、米ドルならusdとする。
off_sessionとconfirmはそれぞれtrueを設定。
off_sessionをtrueにすることで、SetupIntent作成時の認証を利用して、このタイミングでの認証が免除されるようになる。免除の条件を満たしていない場合、エラーが返されることがある。
confirmをtrueにすることで、その場でPaymentIntentの確認が行われ、問題がなければ支払いが行われる。

サポートされている通貨についてはこちら

支払いに失敗した場合はエラーコード(Stripeから返されたエラー)と拒否コード(カード発行会社等から返されたエラー)を取得。

エラーコードについてはこちら
拒否コードについてはこちら

今回は深夜にバッチを走らせて処理を行っているので、翌朝連結アカウントと顧客それぞれに決済エラーお知らせのメールを送信するようにした。

公式APIリファレンス:https://docs.stripe.com/api/payment_intents/retrieve

foreach($user_list as $user){
	// 請求金額計算処理

	try {
		\Stripe\Stripe::setApiKey("プラットフォームのシークレットキー");
	
		\Stripe\PaymentIntent::create(
			[
				"amount" => "請求金額",
				"currency" => "jpy",
				"automatic_payment_methods" => [
					"enabled" => true
				],
				"application_fee_amount" => "プラットフォームに送金する手数料",
				"customer" => "Customer ID",
				"payment_method" => "PaymentMethod ID",
				"off_session" => true,
				"confirm" => true,
			],
			["stripe_account" => "連結アカウントのID"]
		);
	} catch (\Stripe\Exception\CardException $e) {
		$error_code = $e->getError()->code;
		$payment_intent_id = $e->getError()->payment_intent->id;
		$payment_intent = \Stripe\PaymentIntent::retrieve($payment_intent_id);
		$decline_code = $payment_intent->last_payment_error->decline_code;
	
		// 顧客用メール処理
	}
}

// 連結アカウント用メール処理(連結アカウントに紐づく顧客のエラーをまとめて送信)

動作確認

連結アカウントの管理画面で実行結果を確認。

連結アカウントの管理画面左側の支払いをクリック。

正常に決済ができていれば、リストの中に作成した支払いがあるはずなのでクリック。

APIで登録した決済情報が入っているかを確認。

完成

動作確認がすべて終わったので、Cronを設定して作業完了。
Cronがちゃんと動いているかの確認も忘れない。