JAVA云侧SDK设计开发实践

happy young...大约 6 分钟后端开发JAVASDK自动化生成

云侧接口SDK设计思路

SDK设计的初衷,是为了简化商户接入的过程。在此前的支撑沟通中,发现主要的问题在于签名验签等固有机制的处理上,因此需要将该部分逻辑封装,使开发者聚焦于接口业务出入参的处理上。

然而业务当前仍处于快速迭代发展过程中,不断存在新的产品能力及开放接口上线,为了便于后续快速更迭,sdk内接口的新增需要能做到流程体系化——依托于云侧接口的原始设计,可以自动生成新的sdk接口及配套验证demo。

  1. 整个业务对接过程的配置,涉及到全局的对接配置放到一个类中,涉及到单个请求的配置,放到一个类中。
  2. 使用全局配置类初始化client对象,在调用方法时单接口的配置可选传入。
  3. 为了便于商户使用自定义的http client。对外定义了doPost和doGet接口使业务开发者自由实现。
  4. 最终对外暴露一个execute(String httpMethod, String apiUrl, Class rspType, RequestConfig requestConfig, Object requestObj)方法。这里除了requestConfig和requestObj,其他的接口信息,都可以在接口定义中提取到。
  5. 最后使用swagger-codegen插件,配合自定义的mustache模板,生成GenerateApiService.generateFunc方法,该方法内,入参就仅需传入入参及请求配置了。最终暴露到开发者层面的使用也将非常简洁。
  6. 同理,也可以模板化自动生成test用例,使用接口设计时定义的示例入参做连通性访问的测试。

uml: 核心类关系

SDK核心类关系
SDK核心类关系
@startuml
package "customer domain" #DDDDDD {


   class BaseClient {
      <<abstract>>
      - GlobalConfig config
      +BaseClient(config)
      +execute(String httpMethod, String apiUrl, Class<T> rspType, RequestConfig requestConfig,
      Object requestObj)
      +execute(String httpMethod, String apiUrl, Class<T> rspType, Object requestObj)
      +executeexecute(String httpMethod, String apiUrl, Class<T> rspType)
      #doPost(String url, Map<String, String> headers, String requestBody)
      #doGet(String url, Map<String, String> headers)
   }

   class DefaultClient {
      #doPost(String url, Map<String, String> headers, String requestBody)
      #doGet(String url, Map<String, String> headers)
   }
   BaseClient <|-- DefaultClient

   class BaseApiService {
      # BaseClient baseClient
      + BaseApiService(baseClient)
   }
   class GenerateApiService {
      + generateFunc1(requestBody, requestConfig)
      + generateFunc1(param1, param2, requestConfig)
   }
   BaseApiService <|-- GenerateApiService

   class GlobeConfig {
      +String mercNo
      +String priKey
   }
   class RequestConfig {
      +String requestId
      +String sessionKey
   }

}
@enduml

mustache生成示例代码

return baseClient.execute("{{httpMethod}}",
   "{{path}}"
   {{#pathParams}}.replace("{"+"{{paramName}}"+"}",{{paramName}}){{/pathParams}}
   {{#queryParams}}.replace("{"+"{{paramName}}"+"}",{{paramName}}){{/queryParams}},
   {{returnType}}.class,
   requestConfig
   {{#allParams}}{{#isBodyParam}},{{paramName}} {{/isBodyParam}}{{/allParams}}
   );

SDK使用说明

安装

Maven

加入以下依赖

<dependency>
    <groupId>com.huawei.petalpay</groupId>
    <artifactId>pay-java</artifactId>
    <version>1.0.0</version>
</dependency>

调用业务请求接口

以 App 支付预下单为例,先补充商户号等必要参数以构建 config,初始化payClient。 再构建 service 即可调用 aggrPreOrderForApp 发送请求。

/** Native 支付下单为例 */
public class QuickStart {

    // 商户配置
    public static PetalPayConfig getMercConfig() {
        return PetalPayConfig.builder().callerId(MERC_NO) // (必填)商户号
            .privateKey(MERC_PRIVATE_KEY) // (必填) 商户秘钥
            .authId(MERC_AUTH_ID) // (必填) 商户证书序列号
            .signType(SIGN_TYPE) // (选填) 商户公私钥类型,默认RSA加密
            .petalpayPublicKey(HW_PAY_PUBLIC_KEY_FOR_CALLBACK) // (非必填) 验签公钥(和接口级配置needVerifyRsp对应,公钥和商户通知回调验签公钥同一个)
            .domainHost(SERVER_HOST).build();
    }

    public static PreOrderCreateRequest getRequest() {
        return PreOrderCreateRequest.builder()
            .mercOrderNo("20230328" + System.currentTimeMillis()) // 每次订单号都要变
            .mercNo(MERC_NO) // 商户的商户号
            .tradeSummary("Mate50 手机")
            .bizType(
                "100002") // (100001:虚拟商品购买,100002:实物商品购买,100003:预付类账号充值,100004:航旅交通服务,100005:活动票务订购,100006:商业服务消费,100007:生活服务消费,100008:租金缴纳,100009:会员费缴纳,100011:其他商家消费,100037:公共便民服务)
            .currency("CNY")
            .totalAmount(2L)
            .callbackUrl("http://www.abc.com") // 回调通知地址,通知URL必须为直接可访问的URL,要求为http或https(建议)地址。最大长度为512
            .payload("1000222") // 商户预留信息,在查询和回调通知时会原样返回。最大长度255。
            .expireTime(
                "2023-03-28T17:50:12.000+0800") // string 交易过期时间。格式要求:"yyyy-MM-dd'T'HH:mm:ss.SSSZ" 注意:要使用必须传准确的UTC时间
            .allocationType("DELAY_ORDER_ALLOCATION") // 分账类型,NO_ALLOCATION—不分账,DELAY_ORDER_ALLOCATION—延时分账
            .build();
    }

    public static void main(String[] args) {
        DefaultPetalPayClient payClient = new DefaultPetalPayClient(getMercConfig());
        // 配置请求参数
        AggrPay aggrPay = new AggrPay(payClient);
        // 组装对象
        PreOrderCreateRequest preOrderReq = getRequest();
        PreOrderCreateResponse response = null;
        try {
            response = aggrPay.aggrPreOrderForApp(preOrderReq);
        } catch (Exception e) {
            System.out.println(e);
        }
        System.out.println(JsonUtils.obj2Json(response));
    }

}

从示例可见,使用 SDK 不需要计算请求签名和验证应答签名。

回调通知

首先,你需要在你的服务器上创建一个公开的 HTTP 端点,接受来自华为支付的回调通知。 当接收到回调通知,使用VerifyTools.getCallbackResult方法来验证回调通知。 并实现CallBackHandleInterface接口来处理回调结果。 使用示例如下:

public class PayDemoMerchantController {
    /**
     * 华为支付通知回调签名公钥
     */
    public static final String HW_PAY_PUBLIC_KEY_FOR_CALLBACK = "";

    /**
     * 支付回调模拟接口(不同的场景,用不同的接口处理)
     *
     * @param request 入参
     * @return CallBackBaseResponse
     */
    @PostMapping(value = "/v1/transation/result", produces = MediaType.APPLICATION_JSON_VALUE)
    public CallBackBaseResponse transationResultNotify(HttpServletRequest request) {
        // TransResultCallbackReq-支付回调实体类
        return VerifyTools.getCallbackResult(request, HW_PAY_PUBLIC_KEY_FOR_CALLBACK, reqString -> {
            NotifyPaymentReq callbackReq = JSONObject.parseObject(reqString, NotifyPaymentReq.class);
            // 商户自行业务处理
            doProcess(callbackReq);
        });
    }

    /**
     * 业务处理
     *
     * @param reqBody
     */
    private void doProcess(Object reqBody) {
        log.info("Please write merchant business process here");
    }
}

目前不同通知业务结果的通知类存在差异,对应的映射关系如下: NotifyPaymentReq: 支付及代扣结果回调 NotifyRefundReq: 退款结果回调 NotifyContractReq: 签约结果回调 NotifyAllocReq: 分账结果回调 NotifyReclaimAllocReq: 分账回收结果回调 NotifyCombinedTransactionReq: 合单支付结果回调

敏感信息加解密

为了保证通信过程中敏感信息字段(如用户的住址、银行卡号、手机号码等)的机密性, 微信支付要求加密上送的敏感信息。对应的字段在api接口文档中标注。 对应的接口调用方法如下:

// 组装对象
String sessionKey=SM4Util.getSM4GCMSessionKey();
    String message="plainText";
    RegisterSubmercReq req=RegisterSubmercReq.builder().message(SM4Util.getSM4GCMContent(sessionKey,message)).build();
    RequestConfig config=RequestConfig.builder().publicKeyForSessionKey(HW_PUBLIC_KEY_FOR_SESSIONKEY).sessionKey(sessionKey).build();
    MgmtSubmercRsp response=null;
    try{
    response=partnerMerchantMgmt.partnerMgmtSubmercRegister(req,config);
    }catch(Exception e){
    System.out.println(e);
    }
    System.out.println(JsonUtils.obj2Json(response));

自定义httpClient

SDK 使用 [HttpClient] 作为默认的 HTTP 客户端。 开发者可以直接使用 DefaultPetalPayClient来发起http请求。 开发者如果需要自定义接口请求的client,以做请求中的日志打印等操作,可以通过继承PetalpayClient来实现:

public class DefaultPetalPayClient extends PetalPayClient {

    public DefaultPetalPayClient(PetalPayConfig petalPayConfig) {
        super(petalPayConfig);
    }

    @Override
    public String doPost(String url, Map<String, String> headers, String requestBody) throws Exception {
        // todo
    }

    @Override
    public String doGet(String url, Map<String, String> headers, String requestBody) throws Exception {
        // todo
    }
}

新增或拓展业务接口

开发者如果未及时更新SDK,需要使用最新的http接口,可直接调用petalpayClient的execute方法,进行接口请求。 对应的方法签名如下:

execute(String httpMethod, String apiUrl,Class<T> rspType); 
execute(String httpMethod, String apiUrl, Class<T> rspType, Object requestObj); 
execute(String httpMethod, String apiUrl, Class<T> rspType, RequestConfig requestConfig, Object requestObj);
上次编辑于:
贡献者: Yangxi
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.14.6