目录

本章节主要介绍接入华为支付的客户端开发步骤,以帮助开发者快速了解华为支付提供的客户端接口及其调用方法。请开发者参考“应用开发-场景介绍-支付场景举例”,基于接口使用场景,自行选择相应的开发章节。

对于使用自定义价格支付pay接口的开发者需要完成如下开发步骤:

    1. 初始化Agent

    2. 调用pay自定义价格支付接口完成支付

    另外对于有自己应用服务器的开发者需要完成支付回调服务器的开发工作,详细步骤参考《服务端开发》章节。

1 初始化Agent

    HMS Agent 套件提供了对HMS Core SDK原生接口的二次封装,处理了部分必要的接入逻辑而不再将它们暴露给开发者,使用封装后的Agent接口接入HMS Core SDK,可大大提升接入效率,降低出错几率。

1.1 开发步骤

    调用init

    请务必在application的onCreate方法中初始化HMS Agent套件。如果没有自己的application类,请创建MyApplication类,并在manifest文件中配置application节点的name属性。
    示例代码:

  1. /**
  2. *初始化HMS Agent
  3. */
  4. public class MyApplication extends Application {
  5.        @Override
  6.        public void onCreate() {        
  7.                super.onCreate();
  8.                HMSAgent.init(this);
  9.        }
  10. }


  1. <application
  2.    android:name=".MyApplication"
  3.    android:allowBackup="true"
  4.    android:icon="@mipmap/ic_launcher"
  5.    android:label="@string/app_name"
  6.    android:roundIcon="@mipmap/ic_launcher_round"
  7.    android:supportsRtl="true"
  8.    android:theme="@style/AppTheme">

    示例代码:    请务必在应用启动后的首个activity的onCreate方法中调用connect接口,确保HMS Core SDK和HMS APK的连接。

  1. /**
  2. * SDK连接HMS
  3. */
  4. HMSAgent.connect(this, new ConnectHandler() {
  5.    @Override
  6.    public void onConnect(int rst) {
  7.        showLog("HMS connect end:" + rst);
  8.    }
  9. });

2 调用pay自定义价格支付接口

2.1 接口说明

    接口名:void HMSAgent.Pay.pay (PayReq request, PayHandler handler),简称pay。

    接口详细定义请参见pay。

    自定义价格pay支付接口仅限于国内开放,适用开发者对单个国家或地区提供的商品或服务自行定价或随时修改价格的场景,开发者无需使用华为PMS系统进行商品全球定价。可以帮助开发者快速实现应用内支付功能。

    自定义价格pay支付接口的支付流程如图4-1所示:

图4-1 pay接口支付流程

 

    说明:请在应用的服务器侧进行支付结果验签。客户端回调结果仅供参考,供单机应用或者游戏进行支付结果验签,且需要使用查询订单接口保证降低掉单率。

 

2.2 开发步骤

    步骤1 封装PayReq对象

    创建并封装PayReq对象。PayReq对象属性的详细信息请参见pay

  1. private PayReq createPayReq(float totalAmount) {
  2.    PayReq payReq = new PayReq();
  3.    /**
  4.     * 生成requestId | Generate RequestID
  5.     *
  6.     */
  7.    DateFormat format = new java.text.SimpleDateFormat("yyyyMMddhhmmssSSS");
  8.    int random= new SecureRandom().nextInt() % 100000;
  9.    random = random < 0 ? -random : random;
  10.    String requestId = format.format(new Date());
  11.    requestId = String.format("%s%05d", requestId, random);
  12.    /**
  13.     * 生成总金额 | Generate Total Amount
  14.     */
  15.    String amount = String.format("%.2f", totalAmount);
  16.    //商品名称 | Product Name
  17.    payReq.productName = "test product";
  18.    //商品描述 | Product Description
  19.    payReq.productDesc = "test product description";
  20.    // 商户ID,来源于开发者联盟,也叫“支付id” | Merchant ID, from the Developer Alliance,     //also known as "Payment ID"
  21.    payReq.merchantId = cpId;
  22.    // 应用ID,来源于开发者联盟 | Application ID, from the Developer Alliance
  23.    payReq.applicationID = appId;
  24.    // 支付金额 | Amount paid
  25.    payReq.amount = amount;
  26.    // 支付订单号 | Payment order Number
  27.    payReq.requestId = requestId;
  28.    // 国家码 | Country code
  29.    payReq.country = "CN";
  30.    //币种 | Currency
  31.    payReq.currency = "CNY";
  32.    // 渠道号 | Channel number
  33.    payReq.sdkChannel = 1;
  34.    // 支付结果回调服务器地址,来源于开发者联盟配置的回调地址| Payment result callback URL
  35.    //from the callback url in the Developer
  36.    payReq.url = "http://www.example.com/pay";
  37.    // 回调接口版本号 | Callback Interface Version number
  38.    payReq.urlVer = "2";
  39.    // 商户名称,必填,不参与签名。会显示在支付结果页面 | Merchant name, must be filled
  40.    //out, do not participate in the signature. will appear on the Pay results page
  41.    payReq.merchantName = "XXX Company (set as your company name here)";
  42.    //分类,必填,不参与签名。该字段会影响风控策略 | Categories, required, do not
  43.    //participate in the signature. This field affects wind control policy
  44.    // X4:主题,X5:应用商店,  X6:游戏,X7:天际通,X8:云空间,X9:电子书,X10:华为学习,X11:
  45.    //音乐,X12 视频, | X4: Theme, X5: App Store, X6: Games, X7: Sky Pass, X8: Cloud
  46.    //Space, X9: ebook, X10: Huawei Learning, X11: Music, X12 video,
  47.    // X31 话费充值,X32 机票/酒店,X33 电影票,X34 团购,X35 手机预购,X36 公共缴费,X39 流量充
  48.    //值 | X31, X32 air tickets/hotels, X33 movie tickets, X34 Group purchase, X35
  49.    //mobile phone advance, X36 public fees, X39 flow Recharge
  50.    payReq.serviceCatalog = "X6";
  51.    //商户保留信息,选填不参与签名,支付成功后会华为支付平台会原样 回调CP服务端 | The        
  52.    //merchant retains the information, chooses not to participate in the signature,  
  53.    //the payment will be successful, the Huawei payment platform will be back to the
  54.    //CP service
  55.    payReq.extReserved = "Here to fill in the Merchant reservation information";
  56.    //对单机应用可以直接调用此方法对请求信息签名,非单机应用一定要在服务器端储存签名私钥,并在
  57.    //服务器端进行签名操作。| For stand-alone applications, this method can be called  
  58.    //directly to the request information signature, not stand-alone application must
  59.    //store the signature private key on the server side, and sign operation on the  
  60.    //server side.
  61.    // 在服务端进行签名的cp可以将getStringForSign返回的待签名字符串传给服务端进行签名 | The
  62.    //CP, signed on the server side, can pass the pending signature string returned by
  63.    //Getstringforsign to the service side for signature
  64.    payReq.sign = PaySignUtil.rsaSign(PaySignUtil.getStringForSign(payReq), pay_priv_key);
  65.    return payReq;
  66. }


但是对于没有服务器的单机应用,开发者可以调用Agent中封装的PaySignUtil.rsaSign(PaySignUtil.getStringForSign(payReq), pay_priv_key)方法获取支付签名信息,此时请开发者自行确保私钥的安全性。对于有服务器的非单机应用,我们强烈建议开发者在自己的应用服务器端保存签名私钥,并在服务器端完成签名操作,避免私钥的泄露,参考《服务端开发》章节。

如果上述示例代码中的payReq.sign无法用PaySignUtil.rsaSign(PaySignUtil.getStringForSign(payReq), pay_priv_key)获取,请参考如下方式获取签名(详见客户端示例代码):

    签名方法:

1. 构造源串

    将支付参数中需要参与签名的参数(参与签名的参数是指payreq中标识需要签名的参数,值为空<包括null和"">的不参与签名),按参数名的ASCII码的增序排序,若遇到相同首字母,则看第二个字母,以此类推。
    例如:

    BankId=JUNNETNET&amount=2.00¬ifyTime=1353317037173&orderId=918370122485714G&payType=2&productName=珍珠项链&requestId=1352286825177&result=0&userName=lilan

2. 生成签名值

(a)将源串使用RSA算法(SHA256WithRSA)进行签名。
(b)将签名后的字符数组经过Base64.encode编码,生成签名值。

 

    步骤2 调用pay接口及回调处理

    支付结果处理示例代码如下,各种情况的处理建议见示例中的注释,具体错误码请参见pay

  1. private void pay() {
  2.    PayReq payReq = createPayReq(amount);
  3.    HMSAgent.Pay.pay(payReq, new PayHandler() {
  4.        @Override
  5.        public void onResult(int retCode, PayResultInfo payInfo) {
  6.            if (retCode == HMSAgent.AgentResultCode.HMSAGENT_SUCCESS && payInfo != null) {

  7.                /

  8.                boolean checkRst = PaySignUtil.checkSign(payInfo, pay_pub_key);
  9.                showLog("pay: onResult: pay success and checksign=" + checkRst);
  10.                if (checkRst) {
  11.                    // 支付成功并且验签成功,发放商品 | Payment successful and successful verification, distribution of goods
  12.                } else {
  13.                    // 签名失败,需要查询订单状态:对于没有服务器的单机应用,调用查询订单接口查询;其他应用到开发者服务器查询订单状态。| Signature failed, need to query order status: For stand-alone applications without servers, call Query order interface query, other application to the Developer Server query order status.
  14.                }
  15.            } else if (retCode == PayStatusCodes.PAY_STATE_CANCEL) {
  16.                // 支付失败,原因是用户取消了支付,可能是用户取消登录,或者取消支付
  17.            } else {
  18.                showLog("pay: onResult: pay fail=" + retCode);
  19.                // 其他错误码意义参照支付api参考 | Other error code meaning reference payment API reference
  20.            }
  21.        }
  22.    });
  23.    // 将requestid缓存,供查询订单 | RequestID Cache for Query order
  24.    addRequestIdToCache(payReq.getRequestId());
  25. }

    支付结果校验说明

    当出现以下情况时需要查询订单实际状态:
       ●  支付过程异常中断
       ●  支付结果码为-1005、30005、30002
       ●  结果码为0但是验签失败
    对于非单机应用(应用有自己的服务器端)由于支持支付结果回调自己的应用服务器,客户端需要访问自己的服务器确认是否支付成功的情况,具体开发者客户端和开发者服务器的支付验证接口协议,由开发者客户端和开发者服务器自行协定。
    
说明:对于单机应用(应用无自己的服务器端)需要调用PaySignUtil.checkSign(payInfo, pay_pub_key)接口进行签名操作,在服务器端完成签名的可在客户端跳过此步骤。

    步骤3(可选)开发者服务器端处理支付回调

    说明:此步骤对于无自己服务器端的单机应用忽略,非单机应用请参考《服务端开发》章节实现自己的服务端支付回调功能开发。