跳轉到主要內容

最新版本更新紀錄

version 2.3.4.jar

1. 指定正掃或反掃支付

SDK 支援指定正掃或反掃支付:
  • CollectionReq.SCAN_TYPE_SCAN:掃碼支付
  • CollectionReq.SCAN_TYPE_QRCODE:QR Code 支付
設定方法:
CollectionReq req = new CollectionReq(Long.parseLong(money));
req.setScan_type(scan_type); // CollectionReq.SCAN_TYPE_SCAN / CollectionReq.SCAN_TYPE_QRCODE
若商戶希望強制使用 QR 碼主掃模式(例如展示靜態碼供客戶掃描),建議使用 SCAN_TYPE_QRCODE。預設為後置鏡頭掃描。

簡介

HaoJin 是一款為商戶提供聚合收款服務的手機應用程式。本文件說明 HaoJin 對外開放的介面,協助第三方應用整合收單功能。 HaoJin 支援以下功能:
  1. 發起付款、退款、查詢多筆交易記錄、交易明細
  2. 查詢交易摘要與通道配置
  3. 處理卡類交易的查詢/取消/調整等流程
Architecture Diagram

安裝與配置

權限設定

請於 AndroidManifest.xml 添加以下權限(需安裝 HaoJin App 才能取得授權):
<uses-permission android:name="com.qfpay.haojin.permission.OPEN_API"/>

整合 JAR

qfpay_haojin_api_x.x.x.jar在此下載最新版本)放入 /libs 目錄,並在 build.gradle 引入。

設定目標應用 ID

Config.setTargetAppId("in.haojin.nearbymerchant.oversea");

Proguard 規則

請在 proguard-rules.pro 添加:
-dontnote com.qfpay.haojin.model.**
-keep class com.qfpay.haojin.model.** {*;}

第三方接口調用範例

收單

調用收單請求:
ITradeAPI mTradeApi = TradeApiFactory.createTradeApi(XXXActivity, this);

// 建立收單請求:CollectionReq 為 SDK 類別
CollectionReq collectionReq = new CollectionReq(100);

// 設定 API 請求參數:如掃碼方式、外部訂單號、卡超時時間等
collectionReq.setScan_type(CollectionReq.SCAN_TYPE_SCAN);  // 掃碼方式
collectionReq.setOut_trade_no("EXT202312345");             // 外部訂單號
collectionReq.setWait_card_timeout(120);                   // 刷卡等待秒數
collectionReq.setPay_method("card_payment");               // 支付方式
collectionReq.setCamera_id(0);                             // 鏡頭選擇(0: 後鏡頭)

// 發送請求(SDK 方法)
int ret = mTradeApi.doTrade(collectionReq);

@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data)
{
    super.onActivityResult(requestCode, resultCode, data);

    CollectionResp collectionResp =
            (CollectionResp) mTradeApi.parseResponse(requestCode, resultCode, data);

    if (collectionResp == null) {
        return;
    }

    if (collectionResp.isSuccess()) {
        Transaction transaction = collectionResp.getPayResult();
    } else {
        Log.e(TAG, "onActivityResult: collection error message is " +
                collectionResp.getErrorMsg());
    }
}
回傳結果可能為 null,請務必進行非空判斷後再存取資料欄位。

退款

調用退款請求:
ITradeAPI mTradeApi = TradeApiFactory.createTradeApi(XXXActivity, this);

// RefundReq 為 SDK 類別,構造時帶入 QF 訂單號
RefundReq refundReq = new RefundReq(qfOrderId); // HaoJin 返回的訂單 ID

// 發送退款請求(SDK 方法)
int ret = mTradeApi.doTrade(refundReq);
解析返回值:
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data)
{
    super.onActivityResult(requestCode, resultCode, data);

    RefundResp refundResp = (RefundResp) mTradeApi.parseResponse(requestCode, resultCode, data);

    if (refundResp == null) {
        return;
    }

    if (refundResp.isSuccess()) {
        Transaction transaction = refundResp.getRefundResult();
    } else {
        // 錯誤處理
    }
}

查詢多筆交易

調用查詢請求:
GetTransListReq getTransListReq = new GetTransListReq();

getTransListReq.setChannels(selectedChannel); // 支付通道,如 weixin/alipay
getTransListReq.setTypes(selectedType);       // 交易類型,如 payment/refund
getTransListReq.setMonth(month);              // 按月查詢
getTransListReq.setStartTime(startTime);      // 自定義開始時間
getTransListReq.setEndTime(endTime);          // 自定義結束時間
getTransListReq.setPageSize(pageSize);        // 分頁大小
getTransListReq.setPageNum(pageNum);          // 分頁頁碼

int ret = mTradeApi.doTrade(getTransListReq);
  1. 請確認通道是否支援
  2. 僅支援兩種交易類型(付款、退款)
  3. 自定義時間區間查詢優先於按月查詢
  4. 時間格式:yyyy-MM-dd HH:mm:ss
  5. 月份格式:yyyyMM
  6. 分頁起始為第 1 頁
若同時傳入時間區間與月份查詢條件,SDK 會優先使用時間區間。月份查詢適合整月報表;時間區間適合精確統計。
解析返回值:
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data)
{
    super.onActivityResult(requestCode, resultCode, data);

    GetTransListResp getTransListResp =
            (GetTransListResp) mTradeApi.parseResponse(requestCode, resultCode, data);

    if (getTransListResp == null) {
        return;
    }

    if (getTransListResp.isSuccess()) {
        List<Transaction> transactions = getTransListResp.getTransList();
    } else {
        // 錯誤處理
    }
}

查詢交易明細

調用查詢請求:
GetTransReq getTransReq = new GetTransReq(qfOrderId); // 訂單號
getTransReq.setOut_trade_no("EXT20230123");           // 可選:外部訂單號

int ret = mTradeApi.doTrade(getTransReq);
解析返回值:
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    GetTransResp getTransResp =
            (GetTransResp) mTradeApi.parseResponse(requestCode, resultCode, data);

    if (getTransResp == null) {
        return;
    }

    if (getTransResp.isSuccess()) {
        Transaction transaction = getTransResp.getTrans();
    } else {
        // 錯誤處理
    }
}

查看交易摘要

調用查看交易摘要請求:
CheckTradeSumReq checkTradeSumReq = new CheckTradeSumReq();
int ret = mTradeApi.doTrade(checkTradeSumReq);
解析返回值:
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    CheckTradeSumResp checkTradeSumResp = (CheckTradeSumResp)
            mTradeApi.parseResponse(requestCode, resultCode, data);
}

查詢交易通道配置(已棄用)

此接口已被標記為棄用,請改用 GetUserConfigReq 以獲得相同功能。
調用查詢請求:
GetChannelConfigReq channelConfigReq = new GetChannelConfigReq();
int ret = getTradeApi().doTrade(channelConfigReq);
解析返回值:
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    GetChannelConfigResp getChannelConfigResp = (GetChannelConfigResp)
            mTradeApi.parseResponse(requestCode, resultCode, data);

    if (getChannelConfigResp == null) {
        return;
    }

    if (getChannelConfigResp.isSuccess()) {
        List<Channel> channels = getChannelConfigResp.getChannels();
    }
}

查詢客戶配置信息

調用查詢請求:
GetUserConfigReq getUserConfigReq = new GetUserConfigReq();
int ret = getTradeApi().doTrade(getUserConfigReq);
解析返回值:
UserConfig userConfig = getUserConfigResp.getUserConfig();

if (userConfig == null) {
    Log.e(TAG, "handleChannelsResp: get user config info failed.");
    return;
}

List<Channel> channels = userConfig.getTransChannels();
int currencyCode = userConfig.getCurrency();
若未能正確取得 userConfig,通常表示授權異常或帳號配置不完整,請聯絡技術支援。

預授權相關

預授權適用於飯店入住、租借設備等場景,可先凍結金額,完成服務後再扣款或取消。

預授權交易扣款

PreAuthTransDeductReq req = new PreAuthTransDeductReq(transId);
int ret = mTradeApi.doTrade(req);

if (ret != Config.ResponseCode.SUCCESS) {
    // 錯誤處理
}
解析返回值:
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    PreAuthTransDeductResp deductResp = (PreAuthTransDeductResp)
            mTradeApi.parseResponse(requestCode, resultCode, data);

    if (deductResp.isSuccess()) {
        Log.i(TAG, "onActivityResult: success");
    }
}

預授權交易取消

PreAuthTransCancelReq preAuthTransCancelReq = new PreAuthTransCancelReq(transId);
int ret = mTradeApi.doTrade(preAuthTransCancelReq);

if (ret != Config.ResponseCode.SUCCESS) {
    // 錯誤處理
}
解析返回值:
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    PreAuthTransCancelResp cancelResp = (PreAuthTransCancelResp)
            mTradeApi.parseResponse(requestCode, resultCode, data);

    if (cancelResp.isSuccess()) {
        Log.i(TAG, "onActivityResult: success");
    }
}

預授權交易列表

int pageSize = 10;
int pageNum = 1;

PreAuthTransListReq preAuthTransListReq = new PreAuthTransListReq(pageSize, pageNum);
int ret = mTradeApi.doTrade(preAuthTransListReq);

if (ret != Config.ResponseCode.SUCCESS) {
    // 錯誤處理
}
解析返回值:
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    PreAuthTransListResp transListResp =
            (PreAuthTransListResp) mTradeApi.parseResponse(requestCode, resultCode, data);

    List<PreAuthTransactions> transactions = transListResp.getTransList();
}

預授權交易詳情

String transId = "123123123123";

PreAuthTransDetailReq preAuthTransDetailReq = new PreAuthTransDetailReq(transId);
int ret = getTradeApi().doTrade(preAuthTransDetailReq);

if (ret != Config.ResponseCode.SUCCESS) {
    // 錯誤處理
}
解析返回值:
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    PreAuthTransDetailResp transDetailResp =
            (PreAuthTransDetailResp) mTradeApi.parseResponse(requestCode, resultCode, data);

    PreAuthTransaction transaction = transDetailResp.getTrans();
}

卡交易相關

卡退款

ITradeAPI mTradeApi = TradeApiFactory.createTradeApi(XXXActivity.this);

CardRefundReq cardRefundReq = new CardRefundReq(qfOrderId);
int ret = mTradeApi.doTrade(cardRefundReq);
解析返回結果:
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    RefundResp refundResp = (RefundResp) mTradeApi.parseResponse(requestCode, resultCode, data);

    if (refundResp == null) {
        return;
    }

    if (refundResp.isSuccess()) {
        Transaction transaction = refundResp.getRefundResult();
    } else {
        // 錯誤處理
    }
}

查詢多筆卡交易

GetCardTransListReq cardTransListReq = new GetCardTransListReq();
int ret = mTradeApi.doTrade(cardTransListReq);
解析返回結果:
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    GetTransListResp getTransListResp =
        (GetTransListResp) mTradeApi.parseResponse(requestCode, resultCode, data);

    if (getTransListResp == null) {
        return;
    }

    if (getTransListResp.isSuccess()) {
        List<Transaction> transactions = getTransListResp.getTransList();
    } else {
        // 錯誤處理
    }
}

查詢卡交易詳情

GetCardTransReq getCardTransReq = new GetCardTransReq(orderId);
int ret = mTradeApi.doTrade(getCardTransReq);
解析返回結果:
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    GetTransResp getTransResp =
        (GetTransResp) mTradeApi.parseResponse(requestCode, resultCode, data);

    if (getTransResp == null) {
        return;
    }

    if (getTransResp.isSuccess()) {
        Transaction transaction = getTransResp.getTrans();
    } else {
        // 錯誤處理
    }
}

卡調整

CardAdjustReq cardAdjustReq = new CardAdjustReq(orderId);
int ret = mTradeApi.doTrade(cardAdjustReq);
解析返回結果:
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    CardAdjustResp cardAdjustResp = (CardAdjustResp)
        mTradeApi.parseResponse(requestCode, resultCode, data);

    if (cardAdjustResp == null) {
        return;
    }

    if (cardAdjustResp.isSuccess()) {
        Transaction transaction = cardAdjustResp.getCardTrans();
    } else {
        // 錯誤處理
    }
}

卡清算

CardSettleReq cardSettleReq = new CardSettleReq();
int ret = mTradeApi.doTrade(cardSettleReq);
解析返回結果:
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    CardSettleResp cardSettleResp = (CardSettleResp)
        getTradeApi().parseResponse(requestCode, resultCode, data);

    if (cardSettleResp == null) {
        return;
    }

    List<SettleData> settleDataList = cardSettleResp.getSettleDataList();
}

附錄

交易資訊欄位說明

欄位名稱類型是否必填說明
idString交易識別碼
amtLong交易金額
timeString交易時間(yyyy-MM-dd HH:mm:ss
channelString交易通道(如 weixinalipay
statusInteger交易狀態(詳見下方交易狀態表)
typeString交易類型(payment=支付、refund=退款)
originIdString原始交易識別碼(若為退款必填)
mchntNameString商戶名稱
remarksString備註
confirmCodeString交易確認碼
operatorAccountString操作員帳號
appCodeString刷卡交易申請碼
customerIdString客戶錢包身份(預授權交易用)
customerAccountString客戶錢包帳戶(預授權交易用)
completeTransIdString預授權完成後的新交易 ID
completeTimeString預授權完成時間(yyyy-MM-dd HH:mm:ss

通道資訊欄位說明

欄位名稱類型是否必填說明
nameString通道名稱
descString通道描述

交易狀態代碼表

狀態碼說明
0正常交易
-1 / -2等待支付
-3交易失敗
1沖正
2撤銷
3退款
4部分退款
5預授權凍結
6預授權解凍
7預授權完成

結果代碼說明

結果碼說明
-1未知錯誤
0成功
100客戶端錯誤
101金額錯誤
102AppId 為空
103訂單 Id 為空
104其他參數為空
105用戶取消
106網路錯誤
107用戶未登入
108應用未安裝
109啟動應用失敗
110不支援 API 調用
111時間段錯誤
112不允許跨月查詢
113取得配置資訊失敗
114卡交易調整失敗
115裝置不支援刷卡
116密碼輸入錯誤
200伺服器錯誤
201訂單 ID 不存在
202交易失敗
203餘額不足
204交易確認中
205登入狀態過期
206退款確認中
207退款失敗

歷史版本紀錄

version 2.3.3.jar

  1. Transaction 類別新增 getOut_trade_nogetCardscheme 方法

version 2.3.2.jar

交易相關

  1. 交易支援傳入外部訂單號 out_trade_no
  2. 可設定刷卡等待超時時間 wait_card_timeout(預設 120 秒,必須大於 0)
設定方式:
CollectionReq req = new CollectionReq(Long.parseLong(money));
req.setWait_card_timeout(wait_card_timeout);
req.setOut_trade_no(out_trade_no);

查詢相關

  1. 支援使用 out_trade_no 查詢交易資料
設定方式:
GetTransReq req = new GetTransReq(order_id);
req.setOut_trade_no(out_trade_no);
wait_card_timeout 必須為正整數。若未設定或設定錯誤(如小於等於 0),將自動套用預設 120 秒。

version 2.3.1.jar

  1. 支援指定支付方式:透過 CollectionReq.setPay_method 指定 pay_method
pay_method 可設定值:
  • card_payment:信用卡
  • wx:微信
  • alipay:支付寶
  • payme:PayMe
  • union:銀聯
  • fps:FPS
  • octopus:八達通
  • unionpay_card:銀聯卡
  • amex_card:美國運通
若帳號未開通指定支付方式,即使設定了 pay_method,仍會彈出支付方式選擇視窗。
  1. 支援設定相機:透過 CollectionReq.setCamera_id 設定前/後鏡頭(預設後鏡頭)
camera_id 可用值:
  • 0:CAMERA_PARAM_BACK(後鏡頭)
  • 1:CAMERA_PARAM_FRONT(前鏡頭)
僅支援 0(後鏡頭)與 1(前鏡頭)。其他值將被忽略,預設為後鏡頭。
設定方式範例:
CollectionReq req = new CollectionReq(Long.parseLong(money));
req.setPay_method(current_paymethod);
req.setCamera_id(current_camera);