微信小程序支付 中遇到的坑,请绕行

   微信小程序支付中遇到的问题

1.首先看看微信官方对于微信小程序支付的介绍:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1&index=1

2.从微信官方我们可以看到 微信需要的参数是xml格式的数据,因此第一步我们就需要一个将参数从实体转换为xml格式的工具类:方法的代码如下:

 

package cn.com.webi.paycommon.base.javabase.util;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;

/**
 * Title: XstreamUtil<br>
 * Description: XstreamUtil<br>
 * Company:</br>
 * CreateDate:2019年01月30日 10:14
 *
 * @author james.fxy
 */
public class XstreamUtil {
  /**
   * Title: 对象转换为xml<br>
   * Description: objectToXml<br>
   * CreateDate: 2019/1/30 10:16<br>
   *
   * @category 对象转换为xml
   * @author james.fxy
   * @param obj
   * @return java.lang.String
   * @exception Exception
   */
  public static String objectToXml(Object obj) {
    /**
     * 双下划线转为单下划线
     */
    XStream xStream = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));

    // xstream使用注解转换
    xStream.alias("xml", obj.getClass());
    // 去除属性
    xStream.autodetectAnnotations(true);
    xStream.processAnnotations(obj.getClass());

    return xStream.toXML(obj);

  }

  /**
   * Title: xml转换为对象<br>
   * Description: xmlToObject<br>
   * CreateDate: 2019/1/30 10:16<br>
   *
   * @category xml转换为对象
   * @author james.fxy
   * @param xml
   *          * @param cls
   * @return T
   * @exception Exception
   */
  public static <T> T xmlToObject(String xml, Class<T> cls) {

    XStream xstream = new XStream(new DomDriver());
    xstream.alias("xml", cls);
    // xstream使用注解转换
    xstream.processAnnotations(cls);
    // 去除属性
    xstream.autodetectAnnotations(true);
    return (T) xstream.fromXML(xml);

  }
}

工具类主要的作用是为了实体与xml格式的数据进行转换。

3.需要组出提交到微信后台的支付数据

我们的实体类如下:其中

@XStreamAlias("mch_id") @XStreamAlias是给实体属性起的xml格式的别名(必须与微信的请求格式的参数一致)
package cn.com.webi.paycenter.wechatminiprogram.param;

import com.thoughtworks.xstream.annotations.XStreamAlias;

import lombok.Data;

/**
 * Title: 请求到微信后台的数据<br>
 * Description: 请求到微信后台的数据<br>
 * Company:</br>
 * CreateDate:2019年01月30日 13:34
 *
 * @author james.fxy
 */
@Data
public class MiniProgramOrderRequestParam {
  /**
   * 小程序ID
   */

  private String appid;
  /**
   * 商户号
   */
  @XStreamAlias("mch_id")
  private String mchId;
  /**
   * 随机字符串
   */
  @XStreamAlias("nonce_str")
  private String nonceStr;
  /**
   * 签名类型
   */
  @XStreamAlias("sign_type")
  private String signType;
  /**
   * 签名
   */
  private String sign;
  /**
   * 商品描述
   */
  private String body;
  /**
   * 商户订单号
   */
  @XStreamAlias("out_trade_no")
  private String outTradeNo;
  /**
   * 标价金额 ,单位为分
   */
  @XStreamAlias("total_fee")
  private int totalFee;
  /**
   * 终端IP
   */
  @XStreamAlias("spbill_create_ip")
  private String spbillCreateIp;
  /**
   * 通知地址
   */
  @XStreamAlias("notify_url")
  private String notifyUrl;
  /**
   * 交易类型
   */
  @XStreamAlias("trade_type")
  private String tradeType;
  /**
   * 用户标识
   */
  private String openid;
}

将实体转换为xml

 /**
   * Title: 小程序提交数据请求到微信后台返回数据<br>
   * Description: 小程序提交数据请求到微信后台返回数据<br>
   * CreateDate: 2019/1/31 9:47<br>
   *
   * @category 小程序提交数据请求到微信后台返回数据
   * @author james.fxy
   * @param miniProgramParamToWepay
   * @return java.lang.String
   * @exception Exception
   */
  private String getMiniProgramResultXml(MiniProgramParamToWePay miniProgramParamToWepay)
      throws Exception {
    /**
     * 根据订单号查询金蝶CODE再联查小程序信息表得到小程序相关信息
     */
    OrderResourcePartParam orderResourceParam = wechatProgrammerMapper
        .findMiniProgramInfoByResourceId(miniProgramParamToWepay.getOrderResourceId());
    if (isNull(orderResourceParam)) {
      throw new BaseErrorException(ErrorCodeEnum.ORDER_RESOURCE_PARAM_IS_EMPTY);
    }
    // 通过金蝶code查询微信小程序初始化数据(从缓存获取)
    WechatMiniProgram wechatMiniProgram = getObjectFromRedis(orderResourceParam
        .getNewKingdeeCode());
    if (isNull(wechatMiniProgram)) {
      throw new BaseErrorException(ErrorCodeEnum.ORDER_RESOURCE_PARAM_IS_EMPTY);
    }
    MiniProgramOrderRequestParam miniProgramOrderRequestParam = new MiniProgramOrderRequestParam();
    miniProgramOrderRequestParam.setAppid(miniProgramParamToWepay.getAppid());
    miniProgramOrderRequestParam.setMchId(wechatMiniProgram.getMchId());
    // 业务系统的传入以元为单位,微信后台以分为单位需要乘以100
    miniProgramOrderRequestParam.setTotalFee(orderResourceParam.getTotalFee().multiply(
        new BigDecimal(100)).intValue());
    miniProgramOrderRequestParam.setBody(orderResourceParam.getOrderContent());
    miniProgramOrderRequestParam.setNonceStr(RandomStringGenerator.getRandomStringByLength(32));
    miniProgramOrderRequestParam.setOutTradeNo(RandomStringGenerator.getRandomStringByLength(32));
    miniProgramOrderRequestParam.setNotifyUrl(wePayPortalPath
        + WechatMiniProgramConstant.NOTIFY_URL);
    miniProgramOrderRequestParam.setOpenid(miniProgramParamToWepay.getOpenid());
    miniProgramOrderRequestParam.setSignType(WechatMiniProgramConstant.SIGN_TYPE);
    miniProgramOrderRequestParam.setSpbillCreateIp(miniProgramParamToWepay.getSpbillCreateIp());
    miniProgramOrderRequestParam.setTradeType(WechatMiniProgramConstant.TRADE_TYPE);
    String sign = Signature.getSign(miniProgramOrderRequestParam,
        wechatMiniProgram
            .getSecretKey());
    miniProgramOrderRequestParam.setSign(sign);
  
 
    return XstreamUtil.objectToXml(miniProgramOrderRequestParam);
  }

这样就获取到了需提交到微信后台支付的xml格式数据

注意:这个里有一个问题就是签名,微信默认的签名类型是MD5

我们需要传入需要签名的属性,使用的签名的方法工具,这里面需要传入商户的秘钥进行加签。

package cn.com.webi.paycenter.common;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;

/**
 * Title: 签名算法<br>
 * Description: <br>
 * CreateDate: 2019/1/30 11:15<br>
 *
 * @category 签名算法
 * @author james.fxy
 * @param
 * @return
 * @exception Exception
 */
@Slf4j
public class Signature {

  /**
   * Title: 对象签名<br>
   * Description: getSign<br>
   * CreateDate: 2019/1/30 11:20<br>
   *
   * @category 对象签名
   * @author james.fxy
   * @param o
   *          * @param key
   * @return java.lang.String
   * @exception Exception
   */
  public static String getSign(Object o, String key) throws IllegalAccessException {

    ArrayList<String> list = new ArrayList<String>();
    Class cls = o.getClass();
    Field[] fields = cls.getDeclaredFields();
    for (Field f : fields) {
      f.setAccessible(true);
      if (f.get(o) != null && f.get(o) != "") {
        String name = f.getName();
        XStreamAlias anno = f.getAnnotation(XStreamAlias.class);
        if (anno != null)
          name = anno.value();
        list.add(name + "=" + f.get(o) + "&");
      }
    }
    int size = list.size();
    String[] arrayToSort = list.toArray(new String[size]);
    Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < size; i++) {
      sb.append(arrayToSort[i]);
    }
    String result = sb.toString();
    result += "key=" + key;
    log.info("签名数据:" + result);
    result = MD5.MD5Encode(result).toUpperCase();
    return result;
  }

  /**
   * Title: map格式数据签名<br>
   * Description: getSign<br>
   * CreateDate: 2019/1/30 11:20<br>
   *
   * @category map格式数据签名
   * @author james.fxy
   * @param map
   *          * @param key
   * @return java.lang.String
   * @exception Exception
   */
  public static String getSign(Map<String, Object> map, String key) {

    ArrayList<String> list = new ArrayList<String>();
    for (Map.Entry<String, Object> entry : map.entrySet()) {
      if (entry.getValue() != "") {
        list.add(entry.getKey() + "=" + entry.getValue() + "&");
      }
    }
    int size = list.size();
    String[] arrayToSort = list.toArray(new String[size]);
    Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < size; i++) {
      sb.append(arrayToSort[i]);
    }
    String result = sb.toString();
    result += "key=" + key;
    // Util.log("Sign Before MD5:" + result);
    result = MD5.MD5Encode(result).toUpperCase();
    // Util.log("Sign Result:" + result);
    return result;
  }

}

将获得的xml格式的支付参数提交到微信后台

/**
   * Title: 将成功得到的xml转换为对象<br>
   * Description: 将成功得到的xml转换为对象<br>
   * CreateDate: 2019/1/31 9:53<br>
   *
   * @category 将成功得到的xml转换为对象
   * @author james.fxy
   * @param objectToXml
   * @return cn.com.webi.paycenter.wechatminiprogram.param.MiniProgramOrderResultParam
   * @exception Exception
   */
  private MiniProgramOrderResultParam TranslateSuccessXmlToObject(String objectToXml)
      throws Exception {
   //支付结果
    String result =
        HttpClientUtil.doPostByXml(WechatMiniProgramConstant.WECHAT_BACKGROUND_PAYMENT_PLATFORM,
            objectToXml, null);
    /**
     * 时间戳
     */
    long currentTime = System.currentTimeMillis();
    // 将结果xml格式的数据转换为对象
    MiniProgramOrderResultParam miniProgramOrderResultParam =
        XstreamUtil.xmlToObject(result,
            MiniProgramOrderResultParam.class);
    // 通过appid和商户号mch_id获取 key
    String key = wechatProgrammerMapper.findKeyByMchId(miniProgramOrderResultParam
        .getAppid(), miniProgramOrderResultParam.getMchId());
    // 将对象进行加签
    // 不对商户号进行加签
    miniProgramOrderResultParam.setMchId("");
    WechatMiniSignParam wechatMiniSignParam = new WechatMiniSignParam();
    wechatMiniSignParam.setAppId(miniProgramOrderResultParam.getAppid());
    wechatMiniSignParam.setNonceStr(miniProgramOrderResultParam.getNonceStr());
    wechatMiniSignParam.setPackage_("prepay_id=" + miniProgramOrderResultParam.getPrepayId());
    wechatMiniSignParam.setTimeStamp(currentTime);
    wechatMiniSignParam.setSignType("MD5");
    String signResult = Signature.getSign(wechatMiniSignParam, key);
    miniProgramOrderResultParam.setSign(signResult);
    miniProgramOrderResultParam.setTimeStamp(currentTime);
    return miniProgramOrderResultParam;
  }

获取到支付结果后返回以下参数:

package cn.com.webi.paycenter.wechatminiprogram.param;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.thoughtworks.xstream.annotations.XStreamAlias;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

/**
 * Title: 微信后台接收到的结果<br>
 * Description:微信后台接收到的结果<br>
 * Company:</br>
 * CreateDate:2019年01月30日 13:36
 *
 * @author james.fxy
 */
@Data
public class MiniProgramOrderResultParam {
  /**
   * appid
   */
  @ApiModelProperty(value = "appid", example = "wxc0a4fd0811d1eeb0")
  private String appid;
  /**
   * 商户Id
   */
  @ApiModelProperty(value = "商户Id", example = "1417321002")
  @XStreamAlias("mch_id")
  private String mchId;
  /**
   * 随机字符串
   */
  @ApiModelProperty(value = "随机字符串", example = "jWl3rKyMJD15DQqB")
  @XStreamAlias("nonce_str")
  private String nonceStr;
  @ApiModelProperty(value = "签名结果", example = "A1C737C8C961F10D7DE0D7AE8977569D")
  private String sign;
  /**
   * 预支付Id
   */
  @ApiModelProperty(value = "预支付ID", example = "wx31114531636844876882bcd42020340563")
  @XStreamAlias("prepay_id")
  private String prepayId;
  /**
   * 交易类型
   */
  @ApiModelProperty(value = "交易类型", example = "JSAPI")
  @XStreamAlias("trade_type")
  private String tradeType;
  /**
   * 返回状态码
   */
  @XStreamAlias("return_code")
  private String returnCode;
  /**
   * 返回状态码信息
   */
  @XStreamAlias("return_msg")
  private String returnMsg;
  /**
   * 业务结果
   */
  @XStreamAlias("result_code")
  private String resultCode;
  /**
   * 微信支付时间戳
   */
  private Long timeStamp;
  @XStreamAlias("package")
  private String package_;
  /**
   * 支付类型
   */
  private String signType;
}

其中还有最重要的一点(小程序调起支付API)

https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=5

这里还需要将支付成功的结果进行加签返回给小程序 加签格式微信后台给出如下:

paySign = MD5(appId=wxd678efh567hg6787&nonceStr=5K8264ILTKCH16CQ2502SI8ZNMTM67VS&package=prepay_id=wx2017033010242291fcfe0db70013231072&signType=MD5&timeStamp=1490840662&key=qazwsxedcrfvtgbyhnujmikolp111111) = 22D9B4E54AB1950F51E0649E8810ACD6

获取加签结果的代码如下:

 

package cn.com.webi.paycenter.wechatminiprogram.param;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Data;

/**
 * Title: WechatMiniSignParam<br>
 * Description: WechatMiniSignParam<br>
 * Company:</br>
 * CreateDate:2019年03月05日 17:43
 *
 * @author james.fxy
 */
@Data
public class WechatMiniSignParam {
  private String appId;
  private String nonceStr;
  @XStreamAlias("package")
  private String package_;
  private String signType;
  private Long timeStamp;
}

 

/**
     * 时间戳
     */
    long currentTime = System.currentTimeMillis();

 WechatMiniSignParam wechatMiniSignParam = new WechatMiniSignParam();
    wechatMiniSignParam.setAppId(miniProgramOrderResultParam.getAppid());
    wechatMiniSignParam.setNonceStr(miniProgramOrderResultParam.getNonceStr());
    wechatMiniSignParam.setPackage_("prepay_id=" + miniProgramOrderResultParam.getPrepayId());
    wechatMiniSignParam.setTimeStamp(currentTime);
    wechatMiniSignParam.setSignType("MD5");
    String signResult = Signature.getSign(wechatMiniSignParam, key);
    miniProgramOrderResultParam.setSign(signResult);
    miniProgramOrderResultParam.setTimeStamp(currentTime);
    return miniProgramOrderResultParam;

返回给微信小程序即可支付

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值