概要
Stripe の Invoice で決済まで行わずにオーソリだけ通したかったので、サポートに方法を問い合わせた。
オーソリ/キャプチャは請求書から自動的に生成されたPaymentIntentでは機能しません。
Stripeサポート
Invoice でオーソリだけ通す方法は公式には存在しないらしい(2021年2月18日時点)。
仕事上どうしてもオーソリだけ通す必要があったので Stripe サポートに方法を提案してみたところ、
以下の手順で対応可能と返答いただいたのでメモしておく。
- Invoice を下書きで作成する
- Invoice を確定する
- 確定後、Invoice の金額に応じた PaymentIntent が自動紐付けされる
- PaymentIntent を自前作成してオーソリしつつ、Invoice の metadata に紐付ける
- Invoice の請求(Pay an Invoice)ではなく、metadata に紐付けた
PaymentIntent をキャプチャする(Capture a PaymentIntent) - Pay an Invoice に
paid_out_of_band = trueを渡してInvoiceを支払い済みと見なす
ただし、この方法は請求書に2つの PaymentIntent が作成されることになる。
(手順3で自動紐付けされる PaymentIntent はキャンセルされる前提となる)
自前作成した PaymentIntent のイベントは Invoice に影響を与えないため、
管理が複雑になることから Stripe サポートとしてはあまりお勧めしないらしい。
サンプルコード
PaymentIntentの自前作成〜Invoiceの確定まで
<?php
use Stripe\StripeClient;
class StripeService
{
public $client;
public function __construct()
{
$this->client = new StripeClient( 'YOUR_STRIPE_SECRET_KEY' );
}
public function createInvoice( \Stripe\Customer $customer, $amount )
{
// InvoiceItem の作成
$this->client->invoiceItems->create( [
'customer' => $customer->id,
'amount' => $amount,
'currency' => 'jpy',
'description' => 'サービス利用料',
'tax_rates' => 'txr_xxx',
] );
$params = [
'customer' => $customer->id,
'collection_method' => 'charge_automatically',
'auto_advance' => false,
];
// Invoice の作成
return $this->client->invoices->create( $params );
}
public function authorize( \Stripe\Customer $customer, \Stripe\Invoice $invoice )
{
try {
// オーソリのために PaymentIntent を自前作成
$payment_intent = $this->client->paymentIntents->create( [
'amount' => $invoice->total,
'currency' => 'jpy',
'customer' => $customer->id,
'description' => 'サービス利用料オーソリ',
'payment_method' => $customer->invoice_settings->default_payment_method,
'payment_method_types' => [ 'card' ],
'capture_method' => 'manual',
'confirm' => true,
] );
// 自前作成の PaymentIntent と Invoice を紐付け
$this->client->invoices->update( $stripe_invoice->id, [
'metadata' => [ 'authorized_payment_intent_id' => $payment_intent->id ],
] );
// Invoice の確定
$this->client->invoices->finalizeInvoice( $invoice->id );
return true;
}
catch ( \Throwable $t ) {
// Invoice が確定できなかった場合は削除
// 自前作成した PaymentIntent 側でカード利用枠不足や有効期限切れなどがあった場合
$this->client->invoices->delete( $invoice->id );
return false;
}
}
}<?php
$stripe = new StripeService();
// Customer の取得 (expandでデフォルト設定のクレカも取る)
$customer = $stripe->client->customers->retrieve( 'cus_xxx', [ 'expand' => [
'invoice_settings.default_payment_method',
] );
$invoice = $stripe->createInvoice( $customer, 1000 );
$result = $stripe->authorize( $customer, $invoice );
if ( $result ) {
// 成功時の処理
}
else {
// 失敗時の処理
}<?php
$stripe = new StripeService();
// Invoice の取得
$invoice = $stripe->client->invoices->retrieve( 'in_xxx' );
// キャプチャしつつ Invoice を支払済と見なす
$stripe->client->paymentIntents->capture( $invoice->metadata[ 'authorized_payment_intent_id' ] );
$stripe->client->invoices->pay( $invoice->id, [ 'paid_out_of_band' => true ] );paid_out_of_band = true とするので、Stripeダッシュボード上は「外部で支払い済み」として表記される。

Invoice のメタデータには自前作成した PaymentIntent の ID が入る。
(メタデータに Stripe オブジェクトの ID が入ると自動でリンク化してくれるので便利)
また、Invoice に自動紐付けされた PaymentIntent は「キャンセル済み」になっていることが分かる。

おわりに
冒頭でも書いたとおり、この方法は管理が複雑になることから Stripe サポートとしてはお勧めしないらしいので、
採用する際はよく検討したい。
実際には、業務ロジックがそこまで複雑でない限りは影響無さそうな感じだった。
Invoiceに紐づく決済情報が直感的ではなくなるため、どちらかと言えば
システム側というよりは運営側(Stripe ダッシュボード利用者)との認識合わせが必要になりそう。