云侧服务准备
Payment Kit服务提供了支付系统相关的一系列API接口。为减少API接口的接入工作量,提供了相应java版本的Maven依赖用于云侧服务对接。
开发者可通过Maven集成来完成服务器开发环境的构建,以此来快速使用华为支付提供的API接口。
可下载示例代码-服务端用以快速完成商户服务器接入。
因业务发展需要,API接口响应、通知回调请求字段可能会发生变动,如开发者自行实现验签逻辑,一定要使用原始的字符串转为Map对象进行验签处理后再转换成自定义的业务对象去使用,以确保验签正常通过。
约束与限制
商户使用提供的示例代码接入华为支付前请确保网络已正常连接,可以从华为支付仓库地址正常拉取Maven依赖。
开发环境:JDK 1.8及以上。
集成Maven依赖
在示例代码pom.xml文件中已配置仓库地址,如无法正常拉取依赖,可在Maven配置文件“settings.xml”中添加华为支付的Maven仓库地址。
<mirror>
<id>central</id>
<mirrorOf>central</mirrorOf>
<url>https://developer.huawei.com/repo/</url>
</mirror>
在示例代码pom.xml文件中已引入jar包的Maven坐标。如商户自己构建工程则需在pom.xml文件中引入如下坐标:
<dependency>
<groupId>com.huawei.petalpay</groupId>
<artifactId>pay-java</artifactId>
<version>1.0.0.331</version>
</dependency>
配置初始化
将以下商户相关配置添加到示例代码配置文件src/main/resources/petalpayconfig.properties。
- 商户号,获取方式请参见查询商户号信息。
- 商户私钥,获取途径请参考准备证书。
- 商户私钥签名类型,RSA或SM2。
- 商户证书ID,获取方式请参见上传商户证书。
- 鸿蒙支付服务验签公钥,获取方式请参见下载华为支付证书。
- 鸿蒙支付服务加密公钥(非必选),涉及接口入参敏感字段(接口会做标注)加密时需配置,参见敏感信息处理。
- 商户号关联的APPID,获取方式请参见AppID管理及关联。
配置内容示例如下:
# 商户号
PETALPAY.MERC_NO=121540000***
# 商户私钥
PETALPAY.MERC_PRIVATE_KEY=MIIJQwIBADAN=9w0BAQEFAASCCS0wg******************************CldcDlDCSsdfDceCSDr+RyvJdfcXssdEA=
# 商户证书ID
PETALPAY.MERC_AUTH_ID=101540200089***
# 商户私钥签名类型
PETALPAY.SIGN_TYPE=RSA
# 鸿蒙支付服务域名
PETALPAY.SERVER_HOST=https://petalpay-developer.cloud.huawei.com.cn
# 鸿蒙支付服务验签公钥
PETALPAY.HW_PAY_PUBLIC_KEY_FOR_CALLBACK=6D015316F09CB747E4467******************************DB46DA4BD0960ADD500D84912
# 鸿蒙支付服务加密公钥(非必选)
PETALPAY.HW_PUBLIC_KEY_FOR_SESSIONKEY=042A7D32FA19C29D3E722D6C4ACAC0B******************************E5A5B1C8120DAC9882E4B093B9CE7A38296F87F41747D319A
# 商户号关联的APPID
PETALPAY.APPID=111831***
业务接口请求
-
获取发起请求客户端对象
可通过工具类MercConfigUtil提供的方法getMercConfig快速获取PetalPayConfig对象来构建请求客户端,对应配置项获取及配置参见配置初始化。
// 商户配置public static PetalPayConfig getMercConfig() {return PetalPayConfig.builder().callerId(MERC_NO) // (必填)商户号.appId(APP_ID) // (必填)商户号关联的APPID.privateKey(MERC_PRIVATE_KEY) // (必填)商户私钥.authId(MERC_AUTH_ID) // (必填)商户证书ID.signType(SIGN_TYPE) // (选填)商户公私钥类型,默认RSA加密.petalpayPublicKey(HW_PAY_PUBLIC_KEY_FOR_CALLBACK) // (非必填)验签公钥(和接口级配置needVerifyRsp对应,公钥和商户通知回调验签公钥同一个).domainHost(SERVER_HOST).build();}方式一:使用默认实现的请求客户端工具类。通过PetalPayConfig构建请求客户端对象示例如下:
private static PetalPayClient payClient = new DefaultPetalPayClient(MercConfigUtil.getMercConfig());方式二:自定义实现请求客户端工具类。开发者如果需要自定义接口请求的client,用于处理请求中的日志打印等操作,可以通过继承PetalPayClient 来实现。示例如下:
public class MercPetalPayClient extends PetalPayClient {public MercPetalPayClient(PetalPayConfig petalPayConfig) {super(petalPayConfig);}@Overridepublic String doPost(String url, Map<String, String> headers, String requestBody) throws Exception {// todo}@Overridepublic String doGet(String url, Map<String, String> headers, String requestBody) throws Exception {// todo}}-
需要使用最新开放的API接口,如示例代码未及时更新,未找到默认提供用于接口请求的对象信息,可直接调用PetalPayClient的execute方法构建接口请求。
-
由于PetalPayClient.execute()方法及DefaultPetalPayClient实现涉及通过SecureRandom.getInstanceStrong()获取安全随机数,如果服务器熵值不足,可能会导致请求阻塞,以下处理方式可供参考:
方式一:通过三方服务补充服务器熵值。以下为通过haveged服务补充熵值参考。
# 查看熵值cat /proc/sys/kernel/random/entropy_avail# 安装havegedyum install haveged# 启动havegedsystemctl start haveged.service# 开启haveged服务开机自启动systemctl enable haveged.service方式二:开发者参照DefaultPetalPayClient自定义实现请求客户端工具类并通过RequestConfig对象tranceId字段(建议每次请求都更新该字段)来主动传递tranceId,避免自动通过SecureRandom.getInstanceStrong()生成安全随机数导致请求阻塞。
public <T> T execute(String httpMethod, String apiUrl, Class<T> rspType, RequestConfig requestConfig, Object requestObj) throws Exception;
-
-
组装请求参数
预下单请求参数组装示例如下:
public static PreOrderCreateRequestV2 getPreOrderCreateRequestV2() {return PreOrderCreateRequestV2.builder().mercOrderNo("pay-example-" + System.currentTimeMillis()) // 每次订单号都要变,请将pay-example-修改为商户自己的订单前缀.appId(MercConfigUtil.APP_ID) // appId,需要配置为与商户绑定的正确的appId.mercNo(MercConfigUtil.MERC_NO) // 商户的商户号.tradeSummary("请修改为对应的商品简称") // 请修改为商品简称.totalAmount(2L).callbackUrl("https://www.xxxxxx.com/hw/pay/callback") //回调通知地址,通知URL必须为直接可访问的URL,要求为https地址。最大长度为512。请替换为格式正确的结果通知回调地址。.build();} -
请求业务接口
不同API接口调用URI不一样,详情请参见API参考文档。
APP预下单请求示例如下:
public CommonResponse aggrPreOrderForAppV2() {// 组装对象PreOrderCreateRequestV2 preOrderReq = getPreOrderCreateRequestV2();PreOrderCreateResponse response = null;try {response = payClient.execute("POST", "/api/v2/aggr/preorder/create/app", PreOrderCreateResponse.class,preOrderReq);} catch (Exception e) {// todo 异常处理log.error("request error ", e);return CommonResponse.buildErrorRsp(e.getMessage());}if (!validResponse(response)) {// todo 异常处理log.error("response is invalid ", response);return CommonResponse.buildFailRsp(response);}// 获取prepayId构建orderStr返回return CommonResponse.buildSuccessRsp(payClient.buildOrderStr(response.getPrepayId()));}public static boolean validResponse(BaseGwRspWithSign rsp) {return rsp != null && "000000".equals(rsp.getResultCode());}
通知回调处理
可使用VerifyTools.getCallbackResult方法自动处理回调结果验签并返回响应给华为支付服务器以及实现CallBackHandleInterface接口来处理回调结果。
通知回调处理示例如下:
public CallBackBaseResponse transactionResultNotify(@RequestBody Object callbackRequest) {
String callbackStr = JSONObject.toJSONString(callbackRequest);
return VerifyTools.getCallbackResult(callbackStr, MercConfigUtil.HW_PAY_PUBLIC_KEY_FOR_CALLBACK, new CallBackHandleInterface() {
@Override
public void fail(CallBackBaseResponse response, String reqString) {
// 未获取到字节流或者验签失败
// 商户自己业务处理
log.error("CallBack failed: ", response != null ? response.getResultCode() : null);
}
@Override
public void success(String reqString) {
NotifyPaymentReq callbackReq = JSONObject.parseObject(reqString, NotifyPaymentReq.class);
// 验签成功,商户自己业务处理
}
});
}
关于通知回调更多具体要求可参考通知回调接口说明。
敏感信息处理
为了保证API接口请求通信过程中敏感信息字段(如用户的住址、银行卡号、手机号码等,涉及加密字段会在具体API接口中标注)的机密性,鸿蒙支付服务要求加密上送。 如开发者使用对应业务接口涉及字段加密,请联系华为侧工程师获取对应的SM2加密公钥(合作咨询可点击此处)。
涉及密钥:
| 密钥类型 | 来源 | 作用 |
|---|---|---|
| SM2 | 华为支付提供。 | 加密开发者生成的SM4密钥。 |
| SM4 | 开发者生成。 | 加密敏感信息字段。 |
处理逻辑:
1、开发者生成SM4对称密钥,用于加密敏感数据内容并作为入参传递给华为支付。
2、开发者使用华为支付提供的SM2密钥加密生成的SM4对称密钥并通过PayMercAuth.sessionKey字段传递给华为支付用于解密敏感信息。
本质为SM4对称密钥加密,示例代码如下:
import com.huawei.petalpay.paymentservice.apiservice.client.model.MgmtSubmercRsp;
import com.huawei.petalpay.paymentservice.core.client.DefaultPetalPayClient;
import com.huawei.petalpay.paymentservice.core.client.PetalPayClient;
import com.huawei.petalpay.paymentservice.core.config.RequestConfig;
import com.huawei.petalpay.paymentservice.core.tools.SM4Util;
import com.huawei.petalpay.paymentservice.example.common.MercConfigUtil;
public class SignRegister {
public static void main(String[] args) {
PetalPayClient payClient = new DefaultPetalPayClient(MercConfigUtil.getMercConfig());
String sessionKey = SM4Util.getSM4GCMSessionKey();
String message = "xxxxxx";
RegisterSubmercReq req = new RegisterSubmercReq(SM4Util.getSM4GCMContent(sessionKey, message));
RequestConfig config = RequestConfig.builder()
.publicKeyForSessionKey(MercConfigUtil.HW_PUBLIC_KEY_FOR_SESSIONKEY)
.sessionKey(sessionKey)
.build();
try {
MgmtSubmercRsp response = payClient.execute("POST", "/api/v1/partner/mgmt/submerc/register",
MgmtSubmercRsp.class, config, req);
} catch (Exception e) {
// todo 异常处理
}
}
static class RegisterSubmercReq {
private String message;
public RegisterSubmercReq(String message) {
this.message = message;
}
}
}