> ## Documentation Index
> Fetch the complete documentation index at: https://sdk.qfapi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 微信掃碼支付

> 本文件說明如何整合微信線上掃碼支付（顧客掃碼付款），包含 QR Code 生成、超時設定與實名認證（選用）。

## 產品說明

微信掃碼支付允許顧客使用 **微信 App 的「掃一掃」功能**，掃描商戶系統產生的 **付款 QR Code** 完成付款。

常見使用場景：

* 桌面網站付款（顯示 QR Code 讓顧客掃碼）
* POS / 收銀台螢幕顯示 QR Code
* Kiosk / 自助機付款

***

## 何時使用

當您的收款流程是「**商戶顯示 QR Code → 顧客用微信掃碼 → 完成支付**」時，使用本支付方式。

***

## API 端點與方法

```http 建立微信掃碼支付交易 theme={null}
POST /trade/v1/payment
```

**PayType：** `800201`

***

## 請求標頭

| Header         | 必填 | 說明                |
| -------------- | -- | ----------------- |
| `X-QF-APPCODE` | 是  | QFPay 分配之 AppCode |
| `X-QF-SIGN`    | 是  | 根據簽名規則產生之 API 簽名  |

***

## 請求參數

| 參數名稱   | 參數編碼           | 必填  | 類型          | 說明                                          |
| ------ | -------------- | --- | ----------- | ------------------------------------------- |
| 交易金額   | `txamt`        | 是   | Int(11)     | 最小幣值單位，例如 100 = 1 元。**建議大於 200** 以降低風控拒付機率。 |
| 交易幣別   | `txcurrcd`     | 是   | String(3)   | 三位貨幣代碼，請參閱支援貨幣。                             |
| 支付類型   | `pay_type`     | 是   | String(6)   | 固定為 `800201`。                               |
| 商戶訂單號  | `out_trade_no` | 是   | String(128) | Merchant Order Number（商戶自定義，必須唯一）。          |
| 交易時間   | `txdtm`        | 是   | String(20)  | Transaction Time：`YYYY-MM-DD hh:mm:ss`      |
| 訂單超時   | `expired_time` | 否   | String(3)   | 單位分鐘，預設 30，範圍 5–120。                        |
| 商品名稱   | `goods_name`   | 否   | String(64)  | 顯示用途；若包含中文請使用 UTF-8。                        |
| 子商戶號   | `mchid`        | 視情況 | String(16)  | 僅代理商或特定模式需要，請按實際開通狀態使用。                     |
| 裝置識別碼  | `udid`         | 否   | String(40)  | 可用於後台追蹤裝置來源（如 POS 機號）。                      |
| 人民幣標記  | `rmb_tag`      | 否   | String(1)   | 若為香港微信錢包且交易幣別為 CNY，需設 `Y`（按實際開通狀態為準）。       |
| 客戶擴展資訊 | `extend_info`  | 否   | Object      | 實名認證資料（選用；僅限中國大陸公民）。                        |

***

## 實名認證（選用）

商戶可選擇啟用 **微信實名認證**（Real-name Verification）。

<Note>
  目前實名認證僅適用於 **中國大陸公民**，需提供：

  * 付款人真實姓名
  * 中國居民身分證號碼
</Note>

規則說明：

* 若商戶已提供身份資訊，付款人微信錢包（如綁定銀行卡）需與提供資料一致
* 若付款人尚未綁定銀行卡，仍可完成付款
* 是否強制實名，依商戶開通設定為準

### extend\_info 範例

```json Real-name Verification Example theme={null}
{
  "user_creid": "430067798868676871",
  "user_truename": "\\u5c0f\\u6797"
}
```

***

## 請求範例

```json WeChat QR Payment Request theme={null}
{
  "pay_type": "800201",
  "txamt": "300",
  "txcurrcd": "HKD",
  "out_trade_no": "3Z6HPCS6RN54J2Y8LUQM8RBDVBA9URYE",
  "txdtm": "2020-04-10 11:45:44",
  "expired_time": "30",
  "udid": "qiantai2"
}
```

***

## 回應參數

| 參數             | 說明                           |
| -------------- | ---------------------------- |
| `qrcode`       | QR Code 內容（需由商戶轉成 QR 圖像展示）   |
| `syssn`        | QFPay Transaction ID         |
| `out_trade_no` | Merchant Order Number        |
| `respcd`       | Return Code（`0000` = 成功建立交易） |
| `resperr`      | 錯誤描述（若有）                     |
| `respmsg`      | 附加訊息（除錯用途）                   |
| `sysdtm`       | System Time                  |
| `txdtm`        | Transaction Time             |
| `txamt`        | 交易金額                         |
| `txcurrcd`     | 交易貨幣                         |
| `chnlsn`       | 通道流水號（若有）                    |

<Warning>
  `respcd = 0000` 僅代表交易建立成功。\
  商戶需展示 `qrcode` 給顧客掃碼，付款完成後的最終狀態請以 Webhook 或查詢 API 確認。
</Warning>

***

## 回應範例

```json WeChat QR Payment Response theme={null}
{
  "sysdtm": "2020-04-10 11:45:44",
  "paydtm": "2020-04-10 11:45:44",
  "txcurrcd": "HKD",
  "respmsg": "OK",
  "qrcode": "weixin://wxpay/bizpayurl?pr=4PsXP5N",
  "pay_type": "800201",
  "cardcd": "",
  "udid": "qiantai2",
  "txdtm": "2020-04-10 11:45:44",
  "txamt": "300",
  "resperr": "success",
  "out_trade_no": "3Z6HPCS6RN54J2Y8LUQM8RBDVBA9URYE",
  "syssn": "20200410000300020086358791",
  "respcd": "0000",
  "chnlsn": ""
}
```

***

## 範例程式碼（多語言）

<Note>
  以下範例邏輯一致，僅語言不同。\
  請依您的實際開發語言選擇對應範例。
</Note>

<CodeGroup>
  ```python Python theme={null}
  # coding=utf8
  import hashlib
  import datetime
  import requests

  environment = "https://test-openapi-hk.qfapi.com"
  app_code = "******"
  client_key = "******"

  def make_req_sign(data, key):
      keys = sorted(data.keys())
      p = []
      for k in keys:
          p.append(f"{k}={data[k]}")
      unsign_str = ("&".join(p) + key).encode("utf-8")
      return hashlib.md5(unsign_str).hexdigest().upper()

  current_time = datetime.datetime.now().replace(microsecond=0).strftime("%Y-%m-%d %H:%M:%S")

  data = {
      "txamt": "300",
      "txcurrcd": "HKD",
      "pay_type": "800201",
      "out_trade_no": "ORDER123456789",
      "txdtm": current_time
  }

  r = requests.post(
      environment + "/trade/v1/payment",
      data=data,
      headers={"X-QF-APPCODE": app_code, "X-QF-SIGN": make_req_sign(data, client_key)}
  )

  print(r.json())
  ```

  ```javascript Node.js theme={null}
  const crypto = require("crypto");
  const request = require("request");

  const environment = "https://test-openapi-hk.qfapi.com";
  const app_code = "******";
  const client_key = "******";

  const payload = {
    txamt: "300",
    txcurrcd: "HKD",
    pay_type: "800201",
    out_trade_no: "ORDER123456789",
    txdtm: "2026-03-03 18:00:00"
  };

  const ordered = {};
  Object.keys(payload).sort().forEach((k) => (ordered[k] = payload[k]));

  const str = Object.keys(ordered).map((k) => `${k}=${ordered[k]}`).join("&") + client_key;
  const sign = crypto.createHash("md5").update(str).digest("hex").toUpperCase();

  request({
    uri: environment + "/trade/v1/payment",
    headers: {
      "X-QF-APPCODE": app_code,
      "X-QF-SIGN": sign
    },
    method: "POST",
    form: payload
  }, function (error, response, body) {
    console.log(body);
  });
  ```

  ```java Java theme={null}
  import java.text.SimpleDateFormat;
  import java.util.Date;
  import java.util.HashMap;
  import java.util.Map;

  public class TestMain {
      public static void main(String[] args) {
          String appcode = "******";
          String key = "******";

          String pay_type = "800201";
          String out_trade_no = "ORDER123456789";
          SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
          String txdtm = df.format(new Date());

          Map<String, String> params = new HashMap<>();
          params.put("pay_type", pay_type);
          params.put("out_trade_no", out_trade_no);
          params.put("txcurrcd", "HKD");
          params.put("txamt", "300");
          params.put("txdtm", txdtm);

          // Generate sign and send request per your project utilities
      }
  }
  ```

  ```php PHP theme={null}
  <?php
  function signParams($fields, $key) {
      ksort($fields);
      $pairs = [];
      foreach ($fields as $k => $v) {
          $pairs[] = $k . "=" . $v;
      }
      return strtoupper(md5(join("&", $pairs) . $key));
  }

  $url = "https://test-openapi-hk.qfapi.com/trade/v1/payment";
  $app_code = "******";
  $app_key = "******";

  $fields = [
    "pay_type" => "800201",
    "out_trade_no" => "ORDER123456789",
    "txcurrcd" => "HKD",
    "txamt" => "300",
    "txdtm" => date("Y-m-d H:i:s")
  ];

  $sign = signParams($fields, $app_key);

  $header = [
    "X-QF-APPCODE: " . $app_code,
    "X-QF-SIGN: " . $sign
  ];

  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, $url);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
  curl_setopt($ch, CURLOPT_POST, 1);
  curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($fields));
  $output = curl_exec($ch);
  curl_close($ch);

  echo $output;
  ?>
  ```
</CodeGroup>

***

## 整合注意事項與最佳實務

<Tip>
  商戶需將回傳的 `qrcode` 內容轉換成 QR Code 圖像（例如 Canvas / QR Library），並展示給顧客掃描。
</Tip>

<Warning>
  當回傳碼為 `1143` 或 `1145` 時，代表交易狀態未確定。\
  商戶必須主動透過交易查詢 API 確認最終狀態，並在必要時進行補單處理。
</Warning>

建議保存以下欄位以利對帳與補單：

* `syssn`（QFPay Transaction ID）
* `out_trade_no`（Merchant Order Number）

***

## 小結

* 適合「顯示 QR Code → 顧客掃碼付款」的收款場景
* 商戶需自行生成並展示 QR Code 圖像
* 可選擇啟用實名認證（僅限中國大陸）
* 建議實作 Webhook / 查詢 API 以確保交易狀態一致
