1.下载官方demo
在服务商下的帮助文档 sdk与demo
demo中的坑,照着demo写wxconfig居然报错,百度后有人实现的比较好,直接复制过来就ok了
如下:
public class WXPayDomainSimpleImpl implements IWXPayDomain {
private WXPayDomainSimpleImpl() {
}
private static class WxpayDomainHolder {
private static IWXPayDomain holder = new WXPayDomainSimpleImpl();
}
public static IWXPayDomain instance() {
return WxpayDomainHolder.holder;
}
public synchronized void report(final String domain, long elapsedTimeMillis, final Exception ex) {
DomainStatics info = domainData.get(domain);
if (info == null) {
info = new DomainStatics(domain);
domainData.put(domain, info);
}
if (ex == null) { //success
if (info.succCount >= 2) { //continue succ, clear error count
info.connectTimeoutCount = info.dnsErrorCount = info.otherErrorCount = 0;
} else {
++info.succCount;
}
} else if (ex instanceof ConnectTimeoutException) {
info.succCount = info.dnsErrorCount = 0;
++info.connectTimeoutCount;
} else if (ex instanceof UnknownHostException) {
info.succCount = 0;
++info.dnsErrorCount;
} else {
info.succCount = 0;
++info.otherErrorCount;
}
}
public synchronized DomainInfo getDomain(final WXPayConfig config) {
DomainStatics primaryDomain = domainData.get(WXPayConstants.DOMAIN_API);
if (primaryDomain == null ||
primaryDomain.isGood()) {
return new DomainInfo(WXPayConstants.DOMAIN_API, true);
}
long now = System.currentTimeMillis();
if (switchToAlternateDomainTime == 0) { //first switch
switchToAlternateDomainTime = now;
return new DomainInfo(WXPayConstants.DOMAIN_API2, false);
} else if (now - switchToAlternateDomainTime < MIN_SWITCH_PRIMARY_MSEC) {
DomainStatics alternateDomain = domainData.get(WXPayConstants.DOMAIN_API2);
if (alternateDomain == null ||
alternateDomain.isGood() ||
alternateDomain.badCount() < primaryDomain.badCount()) {
return new DomainInfo(WXPayConstants.DOMAIN_API2, false);
} else {
return new DomainInfo(WXPayConstants.DOMAIN_API, true);
}
} else { //force switch back
switchToAlternateDomainTime = 0;
primaryDomain.resetCount();
DomainStatics alternateDomain = domainData.get(WXPayConstants.DOMAIN_API2);
if (alternateDomain != null)
alternateDomain.resetCount();
return new DomainInfo(WXPayConstants.DOMAIN_API, true);
}
}
static class DomainStatics {
final String domain;
int succCount = 0;
int connectTimeoutCount = 0;
int dnsErrorCount = 0;
int otherErrorCount = 0;
DomainStatics(String domain) {
this.domain = domain;
}
void resetCount() {
succCount = connectTimeoutCount = dnsErrorCount = otherErrorCount = 0;
}
boolean isGood() {
return connectTimeoutCount <= 2 && dnsErrorCount <= 2;
}
int badCount() {
return connectTimeoutCount + dnsErrorCount * 5 + otherErrorCount / 4;
}
}
private final int MIN_SWITCH_PRIMARY_MSEC = 3 * 60 * 1000; //3 minutes
private long switchToAlternateDomainTime = 0;
private Map domainData = new HashMap();
}
2.统一下单字段详解
首先理解服务商与普桶商户的区别,普通商户只有一个appid,key,mch_id,open_id 而服务商有2套,带sub_的是客户的小程序信息,而appid,key,mch_id,open_id则是服务商的信息
所以在写客户端时一定要清楚此时的配置文件是客户的还是我们服务商自己的 ,还有一定要将客户的app信息加入服务商下
openid不匹配多半是这个原因造成
关于变量key 不是appsercert 而是在服务商安全设置下的appkey,demo需要的证书也在相同位置
签名不正确多半是这个原因造成
最后system_err 卡了许久,也是应该传sub_openid时传了openid造成的
统一下单的demo中默认使用的签名方式是HMAC-SHA256,但是在文档描述中,小程序调起支付只支持MD5,所以建议统一修改为MD5签名
3下单后的二次签名
返回信息中带有prepay_id字段表示下单成功
接下来二次签名,demo中的居然没有在下单中一次做好.
主要是生成paySign,坑就是appId 注意是驼峰命名
第二坑就是此处的appId是上面的sub_appid即是客户端的appid
传错 :会说total_fee为空,其实下单时已传入
然后是package字段的坑 记得传入的是prepay_id=wxxxxxxxxxx 一定要记得 = 号
如下,自己改进的二次签名方法
/**
* @Des:将支付结果再次编码
* @param: [resp, key]
* @return: void
* @auther: ynhj
* @date: 2019-04-09 10:13
*/
public static void handlePaySign(Map resp,String key) throws Exception {
Map wxMap = new HashMap<>();
wxMap.put("appId",resp.get("sub_appid"));
String nonceStr = WXPayUtil.generateNonceStr();
wxMap.put("nonceStr",nonceStr);
resp.put("nonceStr",nonceStr);
wxMap.put("package","prepay_id="+resp.get("prepay_id"));
resp.put("package","prepay_id="+resp.get("prepay_id"));
wxMap.put("signType", "MD5");
resp.put("signType", "MD5");
String timeStamp = String.valueOf(WXPayUtil.getCurrentTimestamp());
wxMap.put("timeStamp",timeStamp);
resp.put("timeStamp",timeStamp);
resp.put("paySign",generateSignature(wxMap,key, WXPayConstants.SignType.MD5));
}
传错会报: 签名不正确
关于退款的回调方法
当我们发起退款,如果传入回调地址,退款成功后请求该地址
参数中主要包含的是req_info,但是这个信息是加密的,官方虽然提供了说明,但是没找到具体的实现,百度后,实现解密
private static final Base64.Decoder decoder = Base64.getDecoder();
public static Map PKCS7Padding(String req_info,String key) throws Exception {
SecretKeySpec secretKeySpec = new SecretKeySpec(WXPayUtil.MD5(key).toLowerCase().getBytes(),"AES");
Cipher pkcs7Padding = Cipher.getInstance("AES/ECB/PKCS5Padding");
pkcs7Padding.init(Cipher.DECRYPT_MODE,secretKeySpec);
String reqInfo = new String(pkcs7Padding.doFinal(decoder.decode(req_info)));
return WXPayUtil.xmlToMap(reqInfo);
}
最后。。一个很实用的方法 ,关于微信请求返回成功的验证
/**
* @Des:返回结果正确
* @param: [resp]
* @return: boolean
* @auther: ynhj
* @date: 2019-04-09 10:13
*/
public static boolean isSuccess(Map resp) {
if(WXPayConstants.SUCCESS.equals(resp.get("return_code")) && WXPayConstants.SUCCESS.equals(resp.get("result_code"))){
return true ;
}else {
return false;
}
}