程序⽀付(服务商模式)解决
前⾔
⽀付官⽅⽂档:
1、第三⽅(服务商)⾃⼰申请账号,⾃⼰开发,⽣成指定内页给特约商户⽤,该模式简称中⼼化模式。
2、以特约商户⾝份申请⼩程序appId,第三⽅完成开发,该模式简称外包模式。
3、通过开放平台第三⽅开发者代特约商户进⾏⼩程序的开发,该模式简称第三⽅模式。
本⽂适⽤于中⼼化模式,服务商⾃⼰开发⼀个⼩程序,但是收款是直接受到对应的特约商户账户中,不收到服务商⾃⼰账户中。
基本流程
服务商再后台申请⼩程序
⼩程序开通⽀付或绑定已开通⽀付的商户号
特约商户需要操作的流程
服务商再⽀付商户后台,为特约商户开通服务商模式下的⽀付账户
特约商户在收到邮件发送的登录账号和密码,登录⾃⼰的⽀付商户后台,绑定⼩程序的appId
提交审核,被拒绝的话再次提交审核,直到审核通过
服务商管理后台中到"待关联商户号"并确认
登录⽀付服务商商户后台,⼿动为特约商户绑定与服务商主体或特约商户主体⼀致的,APP或⼩程序的appId。最多配置5个,⼿⼯配置路径:"服务商商户平台-服务商功能-⼦商户管理-开发配置-特约商户APPID配置"。
⽀付部分
appid:注意这⾥是服务号的appid,不是⼩程序的
mch_id:这⾥是⽤服务商的id 在我的账号⼀栏可以到
sub_appid:这⾥才是⼩程序的appid
sub_mch_id:这⾥对应特约商户号id 付款到对应商户的凭证就是这个在注册特约商户的时候邮件⾥可以
到这⾥建议配置到数据库动态传递
nonce_str:随机字符串
body:这⾥随意填写,也可以填写商品名称
out_trade_no:订单号
total_fee:这⾥必须是整数,单位是分
trade_type:公众平台⽀付或⼩程序⽀付填写:JSAPI,如果是APP的填写:APP
sub_openid:此参数是在发起⽀付前在⼩程序内调起wx.login ⽅法获得code 然后后台通过置换获得⽤户openid
spbill_create_ip:这⾥可以随意填写
notify_url:⽀付回调的地址
sign:此参数为签名参数需要将需要传递的参数进⾏排序并且进⾏md5签名,需要注意的是需添加参数key 即之前修改的服务商api密钥
好了参数分析完毕在后台调⽤统⼀下单⽅法不出意外是成功的,下单代码如下:
Controller部分
@RestController
@RequestMapping("/payment")
public class WxPayController {
private Logger logger = Logger(WxLoginController.class);
@Autowired
private AppletOrderService appletOrderService;
@Autowired
private WxPayProperties wxPayProperties;
@ResponseBody
@PostMapping(value = "/appletWxPay", produces = "application/json;charset=UTF-8")
public Map<Object, Object> appletWxPay(@RequestParam String openId, String totalFee) throws Exception {
logger.info("[WxPayController].penId:{}", openId);
SortedMap<Object, Object> resultMap = new TreeMap<Object, Object>();
String body = "测试";
String out_trade_no = String.Instance().nextId());
PreOrderResult preOrderResult = appletOrderService.placeOrder(body, out_trade_no, totalFee, openId);
System.out.println(preOrderResult);
if(WxContants.SUCCESS.Return_code()) && WxContants.SUCCESS.Result_code())){
resultMap.put("appId", App_id());
resultMap.put("timeStamp", String(System.currentTimeMillis()/1000));
resultMap.put("nonceStr", UUID.randomUUID().toString().replaceAll("-", "").toUpperCase());
resultMap.put("package", "prepay_id="+Prepay_id());
resultMap.put("signType", "MD5");
resultMap.put("sign", ateSignByMd5(resultMap, Key()));
resultMap.put("returnCode", "SUCCESS");
resultMap.put("returnMsg", "OK");
logger.info("【⼩程序⽀付】统⼀下单成功,返回参数:"+resultMap);
}else{
resultMap.put("returnCode", Return_code());
resultMap.put("returnMsg", Return_msg());
logger.info("【⼩程序⽀付】统⼀下单失败,失败原因:{}" + Return_msg());
}
logger.info("[WxPayController].CodeUrl:{}", Code_url());
return resultMap;
}
}
Serivice部分
@Service
public class AppletOrderServiceImpl implements AppletOrderService{
@Autowired
private WxPayProperties wxPayProperties;
@Override
public PreOrderResult placeOrder(String body, String out_trade_no, String total_fee, String openId) throws Exception {
// ⽣成预付单对象
PreOrder preOrder = new PreOrder();
preOrder.App_id());
preOrder.setMch_Mch_id());
preOrder.setSub_Sub_app_id());
preOrder.setSub_mch_Sub_mch_id());
String nonce_str = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
preOrder.setNonce_str(nonce_str);
preOrder.setBody(body);
preOrder.setOut_trade_no(out_trade_no);
preOrder.setTotal_fee(new BigDecimal(total_fee));
preOrder.setSpbill_create_Spbill_create_ip());
preOrder.setNotify_Notify_url());
preOrder.setTrade_type(WxContants.TRADE_TYPE);
preOrder.setSub_openid(openId);
SortedMap<Object, Object> p = new TreeMap<Object, Object>();
p.put("appid", App_id());
p.put("mch_id", Mch_id());
p.put("sub_appid", Sub_app_id());
p.put("sub_mch_id", Sub_mch_id());
p.put("body", body);
p.put("nonce_str", nonce_str);
p.put("out_trade_no", out_trade_no);
p.put("total_fee", total_fee);
p.put("spbill_create_ip", Spbill_create_ip());
p.put("notify_url", Notify_url());
p.put("trade_type", WxContants.TRADE_TYPE);
p.put("sub_openid", openId);
// 签名
String sign = ateSignByMd5(p, Key());
preOrder.setSign(sign);
String xml = XmlUtil.object2Xml(preOrder, PreOrder.class);
// 调⽤下单地址
String returnXml = HttpUtil.sendPost(WxContants.pay_url, xml);
System.out.println(returnXml);
// XML转换为Object
PreOrderResult preOrderResult = (PreOrderResult) l2Object(returnXml, PreOrderResult.class);        // XML转换为Object
// ⼀般企业开发中⽀付流⽔⼊库,⽀付状态更新这些都需要做,此出省略
return preOrderResult;
}
@Override
public PayResult getWxPayResult(HttpServletRequest request) throws Exception {
InputStream inStream = InputStream();
BufferedReader in = null;
String result = "";
in = new BufferedReader(
new InputStreamReader(inStream));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
PayResult pr = (l2Object(result, PayResult.class);
System.out.String());
return pr;
}
}
⼩程序配置部分
@Component
public class WxPayProperties {
@Value("${wxpay.app_id}")
private String app_id;
@Value("${wxpay.sub_app_id}")
private String sub_app_id;
@Value("${wxpay.spbill_create_ip}")
private String spbill_create_ip;
@Value("${wxpay.key}")
private String key;
@Value("${h_id}")
private String mch_id;
@Value("${wxpay.sub_mch_id}")
private String sub_mch_id;
@Value("${ify_url}")
private String notify_url;
// 此处省略get/set⽅法支付分怎么开通
.
..
}
返回数据
{
"appId": "wxe670bb9ea4775345",
"nonceStr": "536D9056202D4292A909392320E2E5BB",
"package": "prepay_id=wx13143641616855cfa3275610dd2a070000",
"returnCode": "SUCCESS",
"returnMsg": "OK",
"sign": "C512D4025134C356BFA58A2F5699E198",
"signType": "MD5",
"timeStamp": "1610519802"
}
⼩程序端根据后台返回的参数,拉起⽀付,代码如下:
'timeStamp': res.data.timeStamp,
'nonceStr': Str,
'package': res.data.package,
'signType': res.data.signType,
'paySign': res.data.sign,
'success':function(res){},
'fail':function(res){},
'complete':function(res){}
})
点击⽀付,总算是来到了这⼀步:
过程中,可能会遇到如下问题:
出现这个错误的原因是签名不正确,多检查检查是哪⼀步出现了问题。
最后
⼀路踩了不少坑,总算还是成功了,因此将解决⽅法记录下来,后⾯做⼩程序⽀付功能的⼩伙伴可以避免踩坑。