> ## 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.

# 支付寶 Web 支付

> 支付寶 Web 支付（香港與海外）整合指南

客戶可在商戶網站上使用支付寶完成交易。使用者掃描顯示的二維碼確認金額並付款。付款完成後，系統可透過 `return_url` 參數將使用者導回商戶指定頁面。支付寶以人民幣即時從用戶錢包扣除金額，QFPay 則以港幣或本地貨幣結算給商戶。

***

## HTTP 請求

* **方法**：`POST`
* **端點**：`../trade/v1/payment`

***

## PayType 對應類型

| 編碼       | 描述                  |
| -------- | ------------------- |
| `801101` | 支付寶線上掃碼支付（支付寶直接結算）  |
| `801501` | 支付寶線上掃碼支付（QFPay 結算） |

***

## 請求參數

| 參數名稱    | 參數代碼           | 必填 | 資料型別        | 描述                                                         |
| ------- | -------------- | -- | ----------- | ---------------------------------------------------------- |
| 訂單金額    | `txamt`        | 是  | Int(11)     | 以最小單位（如 100 = \$1）計算。建議大於 200 以避免風控。                       |
| 幣種      | `txcurrcd`     | 是  | String(3)   | 請參閱 [幣種列表](/zh-hant/integration/api-reference/currencies)。 |
| 支付方式    | `pay_type`     | 是  | String(6)   | 海外商戶：`801101`；香港商戶：`801501`。                               |
| 外部訂單編號  | `out_trade_no` | 是  | String(128) | 商戶自訂交易編號，於同一商戶帳戶中必須唯一。                                     |
| 交易時間    | `txdtm`        | 是  | String(20)  | 時間格式：`YYYY-MM-DD hh:mm:ss`                                 |
| 過期時間    | `expired_time` | 否  | String(3)   | 單位分鐘，預設 30 分鐘，允許範圍為 5–120 分鐘。                              |
| 商品名稱    | `goods_name`   | 否  | String(64)  | 最多 20 個字元（含中英文及數字）。若為中文需使用 UTF-8 編碼。                       |
| 子商戶編號   | `mchid`        | 否  | String(16)  | 僅代理商或特定模式需要，請按實際開通狀態使用。                                    |
| 設備唯一 ID | `udid`         | 否  | String(40)  | 裝置代碼，將顯示於商戶後台。                                             |
| 成功跳轉網址  | `return_url`   | 否  | String(512) | 支付完成後導向的 URL。                                              |

***

## 選填參數

| 支付標記 | `pay_tag` | 預設 `ALIPAYHK`；大陸版使用 `ALIPAYCN` |
| :--- | :-------- | :----------------------------- |

***

## 回應參數

| 參數名稱      | 參數代碼           | 資料型別        | 描述                                                                                                   |
| --------- | -------------- | ----------- | ---------------------------------------------------------------------------------------------------- |
| 支付方式      | `pay_type`     | String(6)   | 回傳支付方式，例如：`801101` / `801501`。                                                                       |
| 系統時間      | `sysdtm`       | String(20)  | 格式：`YYYY-MM-DD hh:mm:ss`，作為結算截止依據。                                                                   |
| 交易時間      | `txdtm`        | String(20)  | 發送請求時的時間。                                                                                            |
| 錯誤訊息      | `resperr`      | String(128) | 如有錯誤則此欄回傳描述。                                                                                         |
| 交易金額      | `txamt`        | Int(11)     | 付款金額（單位為最小單位）。                                                                                       |
| 附加訊息      | `respmsg`      | String(128) | 其他訊息（可用於除錯）。                                                                                         |
| 外部訂單編號    | `out_trade_no` | String(128) | 商戶自訂交易編號。                                                                                            |
| QFPay 訂單號 | `syssn`        | String(40)  | QFPay 系統交易編號。                                                                                        |
| 回應碼       | `respcd`       | String(4)   | `0000` 表成功；`1143` / `1145` 表示仍處理中；其他為失敗。詳見 [交易狀態碼](/zh-hant/integration/api-reference/status-codes)。 |
| 支付連結      | `pay_url`      | String(512) | 用戶端用於生成 QR Code 的支付連結。                                                                               |

***

## 程式碼範例

<CodeGroup>
  ```python Python theme={null}
  #coding=utf8
  import urllib.request, urllib.parse, urllib.error, urllib.request, urllib.error, urllib.parse, hashlib
  import requests
  import datetime
  import string

  # API 憑證
  environment = 'https://test-openapi-hk.qfapi.com'
  app_code = 'D5589D2A1F2E42A9A60C37*********'
  client_key = '0E32A59A8B454940A2FF39**********'

  # 當前時間
  current_time = datetime.datetime.now().replace(microsecond=0)
  print(current_time)

  # 建立簽名函數
  def make_req_sign(data, key):
      keys = list(data.keys())
      keys.sort()
      p = []
      for k in keys:
          v = data[k]
          p.append('%s=%s'%(k,v))
      unsign_str = ('&'.join(p) + key).encode("utf-8")
      s = hashlib.md5(unsign_str).hexdigest()
      return s.upper()

  # 請求參數
  data = {
    'txamt': '10',
    'txcurrcd': 'HKD',
    'pay_type': '801101',
    'out_trade_no': '01234567890123',
    'txdtm': current_time,
    'mchid': 'ZaMVg*****'
  }

  # 發送 POST 請求
  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())
  ```

  ```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="D5589D2A1F2E42A9A60C37*********";
          String key="0E32A59A8B454940A2FF39*********";
          String mchid="ZaMVg*****";

          String pay_type="801101";
          String out_trade_no= "01234567890123";
          SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
          String txdtm=df.format(new Date());
          String txamt="10";
          String txcurrcd="EUR";

          Map<String, String> unsortMap = new HashMap<>();
          unsortMap.put("mchid", mchid);
          unsortMap.put("pay_type", pay_type);
          unsortMap.put("out_trade_no", out_trade_no);
          unsortMap.put("txdtm", txdtm);
          unsortMap.put("txamt", txamt);
          unsortMap.put("txcurrcd", txcurrcd);

          String data=QFPayUtils.getDataString(unsortMap);
          String md5Sum=QFPayUtils.getMd5Value(data+key);

          String url="https://test-openapi-hk.qfapi.com";
          String resp= Requests.sendPostRequest(url+"/trade/v1/payment", data, appcode, key);
          System.out.println(resp);
      }
  }
  ```

  ```javascript Node.js (JavaScript) theme={null}
  // API 憑證
  const environment = 'https://test-openapi-hk.qfapi.com'
  const app_code = 'D5589D2A1F2E42A9A60C37*********'
  const client_key = '0E32A59A8B454940A2FF39*********'

  // 當前時間
  const txdtm = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '')

  // 請求參數
  const payload = {
    txamt: '10',
    txcurrcd: 'HKD',
    pay_type: '801101',
    out_trade_no: String(Math.round(Math.random() * 1000000000)),
    txdtm,
    mchid: 'ZaMVg*****'
  };

  // 建立簽名（按 key 排序後，拼接 & 並加上 client_key，再做 MD5）
  const ordered = {};
  Object.keys(payload).sort().forEach(k => { ordered[k] = payload[k]; });

  const pairs = [];
  for (const k in ordered) {
    if (Object.prototype.hasOwnProperty.call(ordered, k)) pairs.push(`${k}=${ordered[k]}`);
  }
  const sign_str = pairs.join("&") + client_key;

  const crypto = require('crypto')
  const sign = crypto.createHash('md5').update(sign_str).digest('hex').toUpperCase()

  // 發送 POST 請求
  const request = require("request");
  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);
  });
  ```

  ```php PHP theme={null}
  <?php
  function GetRandStr($length){
    $str='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    $len=strlen($str)-1;
    $randstr='';
    for($i=0;$i<$length;$i++){
      $num=mt_rand(0,$len);
      $randstr .= $str[$num];
    }
    return $randstr;
  }

  $url = 'https://test-openapi-hk.qfapi.com';
  $api_type = '/trade/v1/payment';
  $pay_type = '801101';

  $app_code = 'FF2FF74F2F2E42769A4A73*********';
  $app_key = '7BE791E0FD2E48E6926043B*********';
  $now_time = date("Y-m-d H:i:s");

  $fields = array(
    'pay_type' => $pay_type,
    'out_trade_no' => GetRandStr(20),
    'txcurrcd' => 'HKD',
    'txamt' => 2200,
    'txdtm' => $now_time
  );

  ksort($fields);

  $pairs = array();
  foreach($fields as $k=>$v) {
    $pairs[] = $k.'='.$v;
  }
  $fields_string = implode('&', $pairs);

  $sign = strtoupper(md5($fields_string . $app_key));

  $header = array(
    'X-QF-APPCODE: ' . $app_code,
    'X-QF-SIGN: ' . $sign
  );

  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, $url . $api_type);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  curl_setopt($ch, CURLOPT_POST, 1);
  curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);

  $output = curl_exec($ch);
  curl_close($ch);

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

***

## JSON 回應範例

```json theme={null}
{
  "sysdtm": "2020-04-13 10:30:34",
  "paydtm": "2020-04-13 10:30:34",
  "txcurrcd": "HKD",
  "respmsg": "",
  "pay_type": "801101",
  "cardcd": "",
  "udid": "qiantai2",
  "txdtm": "2020-04-13 10:30:34",
  "txamt": "300",
  "resperr": "success",
  "out_trade_no": "4K35N374II7UJJ8RGIAE45O2CVHGHFF0",
  "syssn": "20200413000300020087033882",
  "respcd": "0000",
  "pay_url": "https://globalmapi.alipay.com/gateway.do?total_fee=3.0&secondary_merchant_name=###merchant_name###&out_trade_no=20200413000300020087033882&secondary_merchant_industry=7011&service=create_forex_trade&_input_charset=UTF-8&sign=02beb99974ce6167666280b9727c4444&currency=THB&notify_url=https%3A%2F%2Fo2-hk.qfapi.com%2Fonline-test%2Ftrade%2Falipay%2Fv1%2Fonline_notify&order_valid_time=1800&secondary_merchant_id=2565075&sign_type=MD5&partner=2088631377368888&product_code=NEW_OVERSEAS_SELLER&order_gmt_create=2020-04-13+10%3A30%3A34&return_url=&subject=###merchant_name###",
  "chnlsn": ""
}
```

***

## 備註

<Tip>
  請將 QR Code 或 `iframe` 指向回應中的 `pay_url`。
</Tip>

<Warning>
  請勿重複使用 `out_trade_no`。
</Warning>

<Info>
  若回傳 `respcd` = `1143` / `1145`，請使用 `/trade/v1/query` API 查詢最終交易結果。
</Info>
