コンテンツにスキップ

SB Payment リンク型決済プラグイン

概要

SB Payment Service (SBPS) のリンク型クレジットカード決済をVendureに統合するプラグインです。

  • リンク型決済: 加盟店サイトからSBPS決済画面へリダイレクト
  • XML処理: fast-xml-parserを使用した保守性の高い実装
  • シミュレーションモード: APIキー未取得時でもテスト可能

機能

主要機能

  1. リンク型決済フロー
  2. 注文情報からSB Payment API用のXMLリクエストを生成
  3. SB Payment決済画面へのリダイレクトURL生成
  4. 決済完了後のコールバック処理

  5. XML処理

  6. fast-xml-parserを使用したXMLパース・生成
  7. iconv-liteを使用したUTF-8⇄Shift_JIS変換
  8. SB Payment仕様(XML 1.0、Shift_JIS)に準拠
  9. ハッシュ検証によるセキュリティ確保

  10. シミュレーションモード

  11. APIキー未取得時でも動作確認可能
  12. 成功レスポンスをエミュレート
  13. テスト環境での開発を支援

設定

環境変数

以下の環境変数を設定してください:

# SB Payment API設定
SB_PAYMENT_MERCHANT_ID=your_merchant_id
SB_PAYMENT_SERVICE_ID=your_service_id
SB_PAYMENT_HASH_KEY=your_hash_key

# APIエンドポイント(オプション)
SB_PAYMENT_API_ENDPOINT=https://api.sbpayment.jp/test/
SB_PAYMENT_LINK_URL=https://api.sbpayment.jp/test/link/

# コールバックURL(オプション)
SB_PAYMENT_CALLBACK_URL=http://localhost:3005/sb-payment/callback
SB_PAYMENT_CANCEL_URL=http://localhost:3005/sb-payment/cancel

Vendure設定

apps/vendure-server/src/vendure-config.tsでプラグインを起動し、Vendureのplugins配列に登録します。SbPaymentLinkPlugin.initは環境変数を優先しつつ、未設定項目には開発用デフォルト値を与えます。

import { SbPaymentLinkPlugin } from '@ritsubi/plugins';

plugins: [
  // 省略
  SbPaymentLinkPlugin.init({
    simulatorMode: IS_DEV || !process.env.SB_PAYMENT_MERCHANT_ID,
    apiEndpoint: process.env.SB_PAYMENT_API_ENDPOINT,
    linkUrl: process.env.SB_PAYMENT_LINK_URL,
    merchantId: process.env.SB_PAYMENT_MERCHANT_ID,
    serviceId: process.env.SB_PAYMENT_SERVICE_ID,
    hashKey: process.env.SB_PAYMENT_HASH_KEY,
    callbackUrl: process.env.SB_PAYMENT_CALLBACK_URL,
    cancelUrl: process.env.SB_PAYMENT_CANCEL_URL,
  }),
],

使用方法

決済フロー概要

  1. SbPaymentLinkHandlerが注文データからXMLリクエストを生成し、SB Paymentに送信します。
  2. レスポンスで受け取ったリダイレクトURLをpayment.metadata.public.redirectUrlとして返し、StorefrontはそのURLにリダイレクトします。
  3. 決済完了通知をPOST /sb-payment/callbackで受信し、SbPaymentLinkServiceが検証後に該当PaymentSettledへ遷移させます。
  4. エラー・キャンセル時はSB Payment側でユーザーをcancelUrlに戻し、Vendure側ではログ記録のみ行います。

1. シミュレーションモード(APIキー未取得時)

環境変数が設定されていない場合、自動的にシミュレーションモードで動作します:

  • 決済作成時に即座に成功レスポンスを返す
  • 実際のSB Payment API呼び出しは行わない
  • テスト用のトラッキングIDを生成し、payment.metadata.simulator=trueを付与

2. 本番モード(APIキー取得後)

環境変数を設定すると、実際のSB Payment APIを使用します:

  1. 決済作成: 注文情報からXMLリクエストを生成し、SB Payment APIに送信
  2. リダイレクト: 生成されたリダイレクトURLをpayment.metadata.public.redirectUrlとしてStorefrontへ返却し、フロントエンドで遷移
  3. 決済完了: SB Paymentからのコールバックを受信し、注文を確定

3. コールバックエンドポイント

プラグインは以下のエンドポイントを提供します:

  • POST /sb-payment/callback: 決済完了コールバック(注文状態を更新)
  • POST /sb-payment/cancel: 決済キャンセル通知(ログのみ)

実装構成

SbPaymentLinkService

  • XML生成・送信、ハッシュ検証、コールバック処理を一元管理
  • RequestContextServiceを用いて管理APIコンテキストを生成し、OrderService.transitionPaymentToStatePaymentSettledへ遷移
  • TransactionalConnectionPaymentエンティティを取得し、SB Paymentレスポンスをpayment.metadata.sbPaymentResponseに保管

SbPaymentLinkHandler

  • VendureのPaymentMethodHandlerを実装し、createPaymentでXMLを構築
  • 返却するmetadata.public.redirectUrlはStorefront GraphQLから参照可能
  • cancelPayment/settlePaymentはシミュレーションモードを考慮しつつ即時成功を返す

SbPaymentCallbackController

  • POST /sb-payment/callback/cancelを提供
  • コールバック受信時にSbPaymentLinkService.handleCallbackを呼び出し、決済結果を検証

決済コールバック処理

  1. 受信XML(Shift_JIS)をUTF-8へ変換しfast-xml-parserでパース
  2. res_sps_hashcodeを使ってハッシュ検証
  3. res_tracking_idからPayment.transactionIdを特定し、対応する注文を読み込み
  4. SB Paymentレスポンスをpayment.metadata.sbPaymentResponseへ保存
  5. まだSettledでない場合はOrderService.transitionPaymentToState(..., 'Settled')を呼び出し確定
  6. 成功時はHTTP 200、失敗時は400/500を返却

テスト

接続支援サイトでのテスト

SB Paymentの接続支援サイトを使用してテストできます:

  1. XMLリクエストの生成確認
  2. プラグインが生成するXMLリクエストを確認
  3. ハッシュ値の計算が正しいか検証

  4. エラーハンドリングの確認

  5. 不正なリクエストに対するエラー処理
  6. レスポンスのパース処理

シミュレーションモードでのテスト

APIキーがなくても、以下のテストが可能です:

// 決済作成テスト
const result = await paymentHandler.createPayment(ctx, order, amount, {}, {});

// シミュレーションモードでは即座に成功
expect(result.state).toBe(PaymentState.Authorized);
expect(result.metadata.simulator).toBe(true);

実装詳細

XML処理

fast-xml-parsericonv-liteを使用してXMLを処理:

// XMLパース(Shift_JIS→UTF-8変換後)
const parser = new XMLParser({
  ignoreAttributes: false,
  attributeNamePrefix: '@_',
  textNodeName: '#text',
  parseAttributeValue: true,
  trimValues: true,
});

// XML生成(UTF-8→Shift_JIS変換前)
const builder = new XMLBuilder({
  ignoreAttributes: false,
  attributeNamePrefix: '@_',
  textNodeName: '#text',
  format: true,
  suppressEmptyNode: false,
});

// XML生成フロー
// 1. fast-xml-parserでUTF-8のXMLを生成
const xmlUtf8 = builder.build(data);
// 2. XML宣言を追加(Shift_JIS指定)
const xmlWithDeclaration = `<?xml version="1.0" encoding="Shift_JIS"?>\n${xmlUtf8}`;
// 3. iconv-liteでUTF-8→Shift_JIS変換
const xmlShiftJis = iconv
  .encode(xmlWithDeclaration, 'Shift_JIS')
  .toString('binary');

// XMLパースフロー
// 1. iconv-liteでShift_JIS→UTF-8変換
const xmlUtf8 = iconv.decode(buffer, 'Shift_JIS');
// 2. fast-xml-parserでパース
const data = parser.parse(xmlUtf8);

ハッシュ計算

SB Payment仕様に準拠したハッシュ計算:

private calculateHash(data: string): string {
  return crypto.createHash('sha1')
    .update(data, 'utf-8')
    .digest('hex');
}

エラーハンドリング

  • XMLパースエラー
  • API通信エラー
  • ハッシュ検証エラー
  • 決済失敗時の処理

トラブルシューティング

よくある問題

  1. ハッシュ検証エラー
  2. ハッシュキーが正しく設定されているか確認
  3. ハッシュ計算用文字列の順序を確認

  4. コールバックが受信されない

  5. コールバックURLが正しく設定されているか確認
  6. ファイアウォール設定を確認

  7. シミュレーションモードが有効にならない

  8. 環境変数SB_PAYMENT_MERCHANT_IDが設定されていないか確認
  9. プラグインの初期化オプションを確認

参考資料

技術的な注意事項

文字コード変換

SB Payment APIはXML 1.0形式でShift_JISエンコーディングを要求します:

  • XML生成: UTF-8(JavaScript内部)→ Shift_JIS(API送信)
  • XML解析: Shift_JIS(API受信)→ UTF-8(JavaScript内部)
  • XML宣言: <?xml version="1.0" encoding="Shift_JIS"?>を明示的に指定

実装のポイント

  1. fast-xml-parserはUTF-8で動作するため、Shift_JIS変換が必要
  2. iconv-liteでUTF-8⇄Shift_JIS変換を実現し、XML宣言は<?xml version="1.0" encoding="Shift_JIS"?>を手動追加
  3. HTTPリクエスト/レスポンスはContent-Type: application/xml; charset=Shift_JISを指定
  4. Vendure側ではpayment.metadata.public.redirectUrlでStorefront公開情報と、payment.metadata.sbPaymentResponseで内部監査情報を分離