Logoelepay
ベストプラクティス

WeChat ミニプログラム決済 V3(都度決済)

お客様がお支払いのたびにミニプログラム内で確認を行います

都度決済:お客様がお支払いのたびに WeChat ミニプログラム内で確認を行います。単発の商品注文やスポットのサービスなど、1 件ごとに精算する業務に適しています。一度の契約以降は加盟店様が直接課金する用途(定期配送・サブスクリプションなど)は、WeChat ミニプログラム決済 V3(継続的な課金)をご利用ください。

本ガイドをご利用の前に、elepay へ WeChat ミニプログラム決済 V3 の利用開始をご依頼いただき、アプリが連携テスト可能な状態であることをご確認ください。

1. 連携前の準備

項目加盟店様にご用意いただくもの
WeChat ミニプログラム実際に決済を行うミニプログラムの AppID、および WeChat 側で必要な決済の利用開始と加盟店の紐付け
elepay アプリWeChat Pay V3・ミニプログラム決済チャンネル・対応する AppID が設定済みであることを elepay が確認
API キーアプリのシークレットキー(Secret Key)。加盟店様のサーバーにのみ保管してください
ミニプログラムのログインWeChat のログイン資格情報から現在のユーザーの openid を取得できること
業務サーバー注文の作成・金額の確定・API の呼び出し・業務注文と Charge ID の対応関係の保存ができること
決済結果の受信HTTPS の Webhook を受信できるエンドポイントと、Webhook 署名用シークレットの設定

WeChat ミニプログラムの AppID と elepay のアプリ ID は別の識別子です。決済の作成時にリクエストボディへ WeChat の AppID を渡す必要はありません。elepay はチャンネルに設定済みの AppID を使用します。extra.openid は必ず当該ミニプログラムのものである必要があり、別のミニプログラムや公式アカウントで取得した openid、および unionid は利用できません。

WeChat Pay の注文 API を加盟店様が直接呼び出す必要はなく、paySign をご自身で生成する必要もありません。WeChat Pay への注文作成・決済署名・WeChat Pay のコールバックはすべて elepay が処理します。

2. 全体の流れ

クライアント側のコールバックとサーバー側の通知は、到達順序が一定ではありません。ミニプログラムに表示する最終的な決済結果は、加盟店様のサーバーが確定したステータスに基づいてください。

3. ユーザーの openid を取得する

  1. ミニプログラムで wx.login() を呼び出し、一時的な code を取得します。
  2. その code を加盟店様自身のログイン API へ送信します。
  3. 加盟店様のサーバーが当該ミニプログラムの AppID・AppSecret・code を使って WeChat の code2Session を呼び出します。
  4. 返却された openid を加盟店様の業務ログイン状態に紐付け、決済の作成時にそこから読み出します。

サーバー側の呼び出し形式:

GET https://api.weixin.qq.com/sns/jscode2session?appid=MINI_APP_ID&secret=MINI_APP_SECRET&js_code=LOGIN_CODE&grant_type=authorization_code

WeChat が返すエラーを必ず確認し、openid が取得できない状態のまま決済を作成しないでください。

AppSecret と session_key はミニプログラムへ配布しないでください。elepay の決済作成 API が受け取るのは openid であり、WeChat のログイン code ではありません。

WeChat の API 定義は code2Session 公式ドキュメントをご覧ください。

4. 加盟店サーバーでの決済作成

4.1 エンドポイントと認証

POST https://api.elepay.io/charges
Authorization: Bearer <ELEPAY_SECRET_KEY>
Content-Type: application/json

API は必ず加盟店様のサーバーから呼び出してください。シークレットキーをミニプログラムに含めないでください。Basic 認証も利用でき、その場合はシークレットキーをユーザー名に指定し、パスワードは空にする必要があります(sk_xxx: を Base64 化した値)。パスワード部分が空でない場合は 401 が返ります。詳しくは API ガイドをご覧ください。

V3 はチャンネル設定で選択されるため、リクエストに version フィールドを追加する必要はありません。

4.2 リクエストパラメータ

フィールド要否説明
amount必須整数。JPY は円単位で、100 は 100 円を表します。金額は加盟店様のサーバーで注文から算出してください
currency明示指定を推奨省略時は JPY
paymentMethod必須wechatpay 固定
resource必須mini 固定
orderNo必須加盟店様側の注文番号。最大 50 文字。業務との紐付けに使用します
extra.openid必須対象ミニプログラムにおける、支払うユーザーの openid
capture省略可既定は true。通常の WeChat Pay では true を使用します
description任意注文の説明。最大 1024 文字。商品やサービスが識別できる短い文言を推奨します
metadata任意加盟店様独自のメタデータ

orderNo の文字種に制約はありませんが、決済チャンネル側で特殊文字が拒否される場合があるため、英数字とハイフンのご利用を推奨します。完全な定義は Create charge を、extra の各フィールドは決済 Extra 情報の設定をご覧ください。

本フローでは customerId と sourceId を指定しないでください。 sourceId を指定すると継続的な課金のフローに切り替わり、本ガイドとは起動方法がまったく異なります。

例:

curl 'https://api.elepay.io/charges' \
  -H "Authorization: Bearer ${ELEPAY_SECRET_KEY}" \
  -H 'Content-Type: application/json' \
  --data '{
    "amount": 100,
    "currency": "JPY",
    "paymentMethod": "wechatpay",
    "resource": "mini",
    "orderNo": "MINI-20260915-0001",
    "description": "商品のご注文",
    "capture": true,
    "extra": {
      "openid": "OPENID_FROM_MERCHANT_SESSION"
    }
  }'

作成に成功すると HTTP 201 が返ります。id(Charge ID)と業務注文の紐付けをただちに保存してください。作成の成功は決済オブジェクトを取得したことを意味し、お客様が支払ったことを意味しません。

4.3 決済パラメータの解析

レスポンスの例(関連フィールドのみ。識別子と署名はプレースホルダーです):

{
  "id": "ch_xxxxxxxxxxxxxxxxxxxxxxx",
  "object": "charge",
  "liveMode": true,
  "orderNo": "MINI-20260915-0001",
  "amount": 100,
  "currency": "JPY",
  "status": "pending",
  "credential": "{\"provider\":\"wechatpay\",\"payload\":\"{\\\"appId\\\":\\\"MINI_APP_ID\\\",\\\"timeStamp\\\":\\\"1700000000\\\",\\\"nonceStr\\\":\\\"NONCE\\\",\\\"package\\\":\\\"prepay_id=PREPAY_ID\\\",\\\"signType\\\":\\\"RSA\\\",\\\"paySign\\\":\\\"PAY_SIGN\\\"}\"}"
}

本番モードの credential は平文の 2 層 JSON 文字列であり、2 回の解析が必要です。外側を解析して payload を取り出し、payload 自体も文字列のため、もう一度解析すると決済パラメータになります。SDK による復号は不要です。

加盟店様のサーバーでは次のようにパラメータを取り出し、自社のミニプログラムへ返します:

function buildMiniPaymentResponse(charge, expectedMiniAppId) {
  const credential = JSON.parse(charge.credential);
  const payment = JSON.parse(credential.payload);
  const fields = ['appId', 'timeStamp', 'nonceStr', 'package', 'signType', 'paySign'];
  if (fields.some((field) => typeof payment[field] !== 'string' || !payment[field])) {
    throw new Error('決済パラメータが不完全です');
  }
  if (payment.appId !== expectedMiniAppId || payment.signType !== 'RSA') {
    throw new Error('ミニプログラムの AppID または署名方式が一致しません。テクニカルサポートへご連絡ください');
  }
  if (!payment.package.startsWith('prepay_id=') || payment.package === 'prepay_id=') {
    throw new Error('プリペイド識別子が不正です');
  }
  return { chargeId: charge.id, payment };
}

決済パラメータはそのままご利用いただき、タイムスタンプ・乱数文字列・package・署名を変更しないでください。パラメータが欠落している場合、AppID が一致しない場合、署名方式が RSA でない場合は、テクニカルサポートへご連絡ください。

テストモードの credential は形式が異なります。 テストモードで返るのは暗号化された文字列で、復号するとモック決済ページへの遷移資格情報({"provider":"wechatpay","redirectUrl":"…"})になり、payload も WeChat Pay の決済パラメータも含まれません。そのまま JSON.parse すると失敗します。テストモードではミニプログラムからの決済起動を検証できません。詳しくは第 8 節をご覧ください。

5. ミニプログラムからの決済起動

5.1 決済パラメータの扱い

本フローでは elepay のフロントエンド JavaScript SDK は不要です。本番モード(liveMode=true、resource=mini、sourceId なし)では、前節の手順で決済パラメータを解析したうえで、ミニプログラムのネイティブ API である wx.requestPayment を呼び出します。

5.2 呼び出し例

以下の payment は加盟店様のサーバーから返却されたもので、refreshOrderStatus と showPaymentMessage は加盟店様が実装する業務関数です:

function startPayment(payment, refreshOrderStatus, showPaymentMessage) {
  const miniAppId = wx.getAccountInfoSync().miniProgram.appId;
  if (payment.appId !== miniAppId || payment.signType !== 'RSA') {
    showPaymentMessage('決済設定に問題があります。カスタマーサポートへご連絡ください');
    return;
  }

  wx.requestPayment({
    timeStamp: payment.timeStamp,
    nonceStr: payment.nonceStr,
    package: payment.package,
    signType: payment.signType,
    paySign: payment.paySign,
    success() {
      showPaymentMessage('決済結果を確認しています');
    },
    fail(error) {
      showPaymentMessage(
        error.errMsg === 'requestPayment:fail cancel'
          ? '決済をキャンセルしました'
          : '決済が完了しませんでした。注文状況を確認しています'
      );
    },
    complete() {
      refreshOrderStatus();
    }
  });
}

appId は現在のミニプログラムの同一性を確認するためのもので、wx.requestPayment の呼び出しフィールドとしては渡しません。WeChat Pay は注文作成時の AppID と決済を起動したミニプログラムの一致性を検証します。 異なる場合はその場で失敗するため、上記のチェックで事前に検出してください。V3 で決済を起動する際の signType は RSA 固定で、署名は elepay が生成します。WeChat 公式のミニプログラム決済起動に関する説明もあわせてご覧ください。

success は結果確認を起動するためだけに使用し、そのまま出荷しないでください。キャンセルやクライアント側のエラーが発生した場合も、サーバー側のステータスを照会してください。お客様がミニプログラムを閉じた場合でも、加盟店様のサーバーは独立して決済結果を受信できる必要があります。

6. 最終的な決済結果の確認

6.1 Webhook の受信

elepay に加盟店様のサーバーの Webhook URL・署名用シークレット・charge.succeeded イベントの購読を設定してください。

WeChat Pay はまず elepay に通知し、そこから elepay が加盟店様へ通知します。加盟店様ご自身の通知 URL をミニプログラムの extra に指定する必要はなく、WeChat Pay のコールバックの復号処理を実装する必要もありません。

決済成功通知の例(関連フィールドのみ):

{
  "id": "EVENT_ID",
  "object": "event",
  "liveMode": true,
  "type": "charge.succeeded",
  "createTime": 1700000000000,
  "data": {
    "object": {
      "id": "ch_xxxxxxxxxxxxxxxxxxxxxxx",
      "orderNo": "MINI-20260915-0001",
      "amount": 100,
      "currency": "JPY",
      "status": "captured"
    }
  }
}

処理の要件:

  1. HTTP の生のリクエストボディを使って elepay-Signature を検証してください。パースしてから再度シリアライズした内容で検証しないでください。HTTP ヘッダー名は大文字小文字を区別しません。 フレームワークが提供する大文字小文字を区別しない取得方法をご利用いただき、リテラルの完全一致で取得しないでください。
  2. 署名ヘッダーの形式は t=<秒単位のタイムスタンプ>,sign=<16 進数の署名値> です。Webhook 署名用シークレットを使い、タイムスタンプ + "." + 生のリクエストボディ に対して HMAC-SHA256 を計算し、タイミング攻撃を防ぐため定数時間で比較してください。署名用シークレットは管理画面の Webhook 詳細ページで確認・再生成できます。API Secret Key とは別のものです。
  3. タイムスタンプの許容誤差は既定で 300 秒です(公式 Java SDK Webhook.verifyHeader の既定値)。範囲外のリクエストはリプレイ攻撃対策として拒否してください。
  4. 同一のヘッダーに複数の sign= が含まれる場合があります(シークレットのローテーション期間中)。いずれか 1 つが一致すれば正当とみなしてください。最初の 1 つのみを解析すると、ローテーション期間中に誤って拒否することになります。
  5. イベント種別・Charge ID・業務注文番号・金額・通貨・本番/テストモード・status を照合してください。
  6. 処理に成功したら HTTP 2xx を返してください。失敗時は必ず 4xx または 5xx を返してください。 そうしないとリトライが行われません。
  7. リトライの方針:送信に失敗した場合、5 秒 / 15 秒 / 1 分 / 10 分 / 10 分 の間隔でリトライし、リトライは 5 回(初回配信を除く)、累計 6 回の配信となり、約 21 分で打ち切ります(プラットフォーム既定値。加盟店ごとの個別設定には対応しておりません)。2 回目の送信は最短 5 秒後に到達します。 冪等性の判定期間を分単位の前提で設計しないでください。それ以降は照会 API と突合で補完してください。
  8. イベント ID で重複を排除し、業務注文の層で支払確定と出荷が一度だけ実行されるようにしてください。重複した通知や遅延して到達した通知も処理できる必要があります。
  9. 認識できない type を受信した場合は 2xx を返して無視してください。成功として扱わないでください。

Webhook の設定と検証について詳しくは Webhook ドキュメントをご覧ください。

6.2 ステータスの照会

加盟店様のサーバーから、同一アプリ・同一モードのシークレットキーで照会できます:

curl "https://api.elepay.io/charges/${CHARGE_ID}" \
  -H "Authorization: Bearer ${ELEPAY_SECRET_KEY}"
status業務上の扱い
captured支払済み。金額など注文情報を照合したうえで業務注文を確定します
pending未払い。確認中と表示し、後ほど照会してください
waiting支払待ち(情報入力済み)。確認中と表示し、後ほど照会してください
notified決済準備中(通知済み)。確認中と表示し、後ほど照会してください
uncapturedオーソリ済みで未確定。capture=false の場合のみ発生し、本フローでは現れません
amount_mismatch金額が一致していません。支払成功とみなせません。 出荷せず、人手でご確認ください
failed決済失敗。業務ルールに応じて再実行を許可してください
revoked取消済み。業務ルールに応じて再実行を許可してください
partially_refunded一部返金済み。アフターサービスの処理を行い、新たな支払成功とみなさないでください
refunded返金済み。アフターサービスの処理を行い、新たな支払成功とみなさないでください

照会が返すのは現在のステータスであり、照会のたびに WeChat へリアルタイムに問い合わせるとは限りません。加盟店様のサーバーでは回数を限り、間隔を段階的に広げる照会を推奨します。最終確定は引き続き Webhook にお任せください。完全な定義は Retrieve charge をご覧ください。

ミニプログラムからは加盟店様自身の業務 API のみを呼び出してください。加盟店様のサーバーでは、ログイン中のユーザーが当該注文を参照する権限を持つことを必ず検証してください。

7. 異常時の処理

ケース対応方法
extra.openid が欠落している先に WeChat ログインを完了し、サーバーで有効な openid を紐付けてから注文を作成してください
AppID または openid が一致しない実際に動作しているミニプログラム・openid を取得した AppID・チャンネルに設定された AppID が一致しているかご確認ください
決済パラメータまたは署名のエラーAppID を照合し、パラメータが完全で改変されていないことをご確認ください。解消しない場合は Charge ID とエラー情報をテクニカルサポートへお伝えください
signType が RSA でないご自身で書き換えないでください。Charge ID をテクニカルサポートへお伝えください
決済作成リクエストがタイムアウトするまず保存済みの Charge ID で照会してください。ID を取得できていない場合は Charge 一覧で orderNo を突き合わせるか、サポートへご確認ください。そのまま別の決済を作成しないでください
注文番号が重複するorderNo は安全に再送できる冪等キーではありません。作成済みの決済を照会し、状態をご確認ください
お客様のキャンセルや連打同一注文に対する決済の同時作成を防いでください。再実行の前に元の決済のステータスをご確認ください
フロントは成功だがサーバーはまだ未成功確認中と表示し、通知または照会結果をお待ちください。ただちに出荷しないでください
ステータスが amount_mismatch出荷を停止し、金額の出どころを人手でご確認ください
API がエラーを返すレスポンスの errorCode のみで判別し、message の文言と照合しないでください。詳しくは付録をご覧ください

8. 連携テストについて

テストモードはミニプログラムからの決済起動には利用できません。 テストモードで返るのは暗号化されたモック決済への遷移資格情報で、2 層の JSON.parse もできず、wx.requestPayment に必要なフィールドも含まれません。

Webhook V2 はテストモードではイベントを一切送信しません。 テストモードで通知経路と冪等処理を検証する場合は V1 の Webhook をご利用ください。

実機での決済起動と支払結果の通知は、本番モードでの少額決済による検証が必須です。 実機での連携テストの前に、本番チャンネルが利用可能であることを elepay にご確認ください。

連携テストでは少なくとも以下を網羅してください:

  • 本番モードでの実際の少額決済が成功し、status=captured を確認する
  • お客様が決済をキャンセルする
  • 通信断のあとに復旧して照会する
  • 同一注文への連打
  • 重複した通知、署名が正しくない通知
  • お客様がミニプログラムを閉じたあとでも支払を確定できる
  • 通知のリトライを使い切ったあと、照会でステータスを補完する

9. 連携完了の基準

  • ミニプログラムの AppID・ユーザーの openid・チャンネル設定が一致している。
  • サーバーで決済を作成し、Charge ID を保存し、2 層の JSON の資格情報を正しく解析できる。
  • 実際に signType=RSA の決済パラメータが返り、実機で少額決済を一度完了できる。
  • 加盟店様のサーバーが信頼できる通知または照会によって captured を確定し、業務注文が一度だけ処理される。
  • amount_mismatch などの非成功ステータスを支払成功と誤判定しない。
  • キャンセル・タイムアウト・重複通知のいずれでも、二重決済や二重出荷が発生しない。

付録:エラーレスポンス

すべての API は失敗時に共通の構造を返します(値が null のフィールドは出力されません):

{
  "requestId": "req_xxxxxxxxxxxxxxxxxxxxxxx",
  "errorCode": "M002003",
  "message": "要求されたリソースは存在しません。",
  "parameterName": "orderNo",
  "providerError": {
    "providerKey": "wechatpay",
    "code": "PROVIDER_ERROR_CODE",
    "message": "provider message"
  }
}
フィールド説明
requestIdリクエストの識別子。テクニカルサポートへのお問い合わせ時にお知らせください
errorCodeエラー判別の唯一の根拠です
message人が読むためのメッセージです。プログラムでの判定には使用しないでください
parameterNameパラメータエラー時に該当フィールドを示します
providerError決済チャンネルが返した元のエラー。調査時に有用です

3 つの要件:

  1. errorCode のみで判定してください。 レスポンスには code フィールド(9_xx_xx_10113 のような形式)もありますが、非推奨のため依存しないでください。
  2. message は既定で日本語が返ります。 他の言語が必要な場合はリクエストヘッダーに Accept-Language を指定してください。ja / en / zh-CN / zh-TW に対応しています。
  3. message はサーバー側の文言辞書に由来し、内容が変更される可能性があります。 文字列の一致判定には使用しないでください。

エラーコードの一覧はエラーコードのドキュメントをご覧ください。

最終更新日

このページ