相信技术的力量

Java服务端集成微信支付SDK

微信支付

流程

https://www.cnblogs.com/xyt-0412/p/4953748.html

集成工具包

https://github.com/Wechat-Group/weixin-java-tools
https://github.com/Wechat-Group/weixin-java-tools/wiki/%E5%BE%AE%E4%BF%A1%E6%94%AF%E4%BB%98

微信支付接口调试工具(调试sign是否正确即可)

https://pay.weixin.qq.com/wiki/tools/signverify/

集成步骤

第一步 , 添加依赖

<!--微信支付SDK-->
<dependency>
    <groupId>com.github.binarywang</groupId>
    <artifactId>weixin-java-pay</artifactId>
    <version>3.2.0</version>
</dependency>

第二步, 在application.yml中配置参数

wx:
  pay:
    appId: wxd7b0c5e79c63c714 #微信公众号或者小程序等的appid
    mchId: 1513914421 #微信支付商户号
    mchKey: 1wRzyIaYNlFZRiu307Ih4Nc1QJU5ac7u #微信支付商户密钥
    keyPath: /usr/dev/tomcat8/webapps/cert/apiclient_cert.p12 # p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头)

第三步 , config目录

import lombok.Data;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * wxpay pay properties
 *
 * @author Binary Wang
 */
@Data
@ConfigurationProperties(prefix = "wx.pay")
public class WxPayProperties {
  /**
   * 设置微信公众号或者小程序等的appid
   */
  private String appId;

  /**
   * 微信支付商户号
   */
  private String mchId;

  /**
   * 微信支付商户密钥
   */
  private String mchKey;

  /**
   * apiclient_cert.p12文件的绝对路径,或者如果放在项目中,请以classpath:开头指定
   */
  private String keyPath;

  @Override
  public String toString() {
    return ToStringBuilder.reflectionToString(this,
        ToStringStyle.MULTI_LINE_STYLE);
  }
}

第四步,添加注入

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;

/**
 * @author Binary Wang
 */
@Configuration
@ConditionalOnClass(WxPayService.class)
@EnableConfigurationProperties(WxPayProperties.class)
public class WxPayConfiguration {

  private WxPayProperties properties;

  @Autowired
  public WxPayConfiguration(WxPayProperties properties) {
    this.properties = properties;
  }

  @Bean
  @ConditionalOnMissingBean
  public WxPayService wxService() {
    WxPayConfig payConfig = new WxPayConfig();
    payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppId()));
    payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId()));
    payConfig.setMchKey(StringUtils.trimToNull(this.properties.getMchKey()));
//    payConfig.setSubAppId(StringUtils.trimToNull(this.properties.getSubAppId()));
//    payConfig.setSubMchId(StringUtils.trimToNull(this.properties.getSubMchId()));
    payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath()));

    // 可以指定是否使用沙箱环境
    payConfig.setUseSandboxEnv(false);

    WxPayService wxPayService = new WxPayServiceImpl();
    wxPayService.setConfig(payConfig);
    return wxPayService;
  }

}

第五步 , 统一下单

@Override
@Transactional
public WxPayAppOrderResult wechatpay(CreatePayOrderRequest orderRequest) {
    PayOrder order = getPayOrder(orderRequest);
    logger.info("调用微信支付.订单详情:"+order.toString());
    try {
        WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
        request.setBody("安保费用支付");
        request.setOutTradeNo(order.getUuid());
        request.setFeeType("CNY");
        request.setTotalFee(BaseWxPayRequest.yuanToFen("1"));//元转成分
        request.setSpbillCreateIp("47.99.172.109");
        request.setNotifyUrl("http://47.99.172.109:8080/anbao/order/wechatpaySuccessCallBack");
        request.setSignType(WxPayConstants.SignType.MD5);
        request.setTradeType(WxPayConstants.TradeType.APP);
        WxPayAppOrderResult wxPayAppOrderResult = wxPayService.createOrder(request);
        logger.info("封装返回参数:"+wxPayAppOrderResult.toString());
        return wxPayAppOrderResult;
    } catch (Exception e) {
        logger.error("微信支付失败!订单号:"+ order.getUuid()+",原因:"+ e.getMessage());
        throw new BusinessException(CommonErrorResult.BUSINESS_ERROR, "支付失败");
    }
}

第六步, 成功回调

@Override
@Transactional
public String wechatPaySuccess(HttpServletRequest request, HttpServletResponse response) {
    logger.info("微信订单支付成功回调");
    // 解析结果存储在HashMap
    try {
        String xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
        WxPayOrderNotifyResult result = wxPayService.parseOrderNotifyResult(xmlResult);
        // 结果正确
        String orderId = result.getOutTradeNo();
        //String tradeNo = result.getTransactionId();
        //String totalFee = BaseWxPayResult.fenToYuan(result.getTotalFee());
        //自己处理订单的业务逻辑,需要判断订单是否已经支付过,否则可能会重复调用

        PayOrder payOrder = baseRepository.getObjById(PayOrder.class,orderId);

        //更新订单支付状态
        ...

        return WxPayNotifyResponse.success("处理成功!");
    } catch (Exception e) {
        e.printStackTrace();
        logger.error("微信回调结果异常,异常原因:"+e.getMessage());
        return WxPayNotifyResponse.fail(e.getMessage());
    }
}

带参数的二维码生成

/**
 * 生成医生二维码
 * @param doctorId
 * @return
 */
public static String createQRPic(String doctorId) {
    String barCodeUrl = "";
    String tokenRequest = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APP_ID&secret=APP_SECRET"
            .replace("APP_ID", AppConstants.WX_CONFIG.APP_ID)
            .replace("APP_SECRET", AppConstants.WX_CONFIG.APP_SECRET);
    String tokenResponse = HttpUtil.get(tokenRequest);
    JSONObject tokenJson = JSON.parseObject(tokenResponse);
    if (tokenJson != null) {
        String accessToken = tokenJson.getString("access_token");
        if (StringUtil.isNotEmpty(accessToken)) {
            String ticketUrl = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + accessToken;
            String data = "{\"action_name\": \"QR_LIMIT_STR_SCENE\", \"action_info\": {\"scene\": {\"scene_str\": " + doctorId + "}}}";
            String ticketResponse = null;
            try {
                ticketResponse = HttpUtil.post(ticketUrl, data);
                JSONObject ticketJson = JSON.parseObject(ticketResponse);
                if(null != ticketJson) {
                    String ticket = ticketJson.getString("ticket");
                    barCodeUrl = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + ticket;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    return barCodeUrl;
}

二维码扫码回调

方案

在服务端配置接口,在微信公众平台(基本配置->服务器配置)配置扫码回调接口即可

代码

/**
 * 生成医生二维码
 * @param doctorId
 * @return
 */
public static String createQRPic(String doctorId) {
    String barCodeUrl = "";
    String tokenRequest = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APP_ID&secret=APP_SECRET"
            .replace("APP_ID", AppConstants.WX_CONFIG.APP_ID)
            .replace("APP_SECRET", AppConstants.WX_CONFIG.APP_SECRET);
    String tokenResponse = HttpUtil.get(tokenRequest);
    JSONObject tokenJson = JSON.parseObject(tokenResponse);
    if (tokenJson != null) {
        String accessToken = tokenJson.getString("access_token");
        if (StringUtil.isNotEmpty(accessToken)) {
            String ticketUrl = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + accessToken;
            String data = "{\"action_name\": \"QR_LIMIT_STR_SCENE\", \"action_info\": {\"scene\": {\"scene_str\": " + doctorId + "}}}";
            String ticketResponse = null;
            try {
                ticketResponse = HttpUtil.post(ticketUrl, data);
                JSONObject ticketJson = JSON.parseObject(ticketResponse);
                if(null != ticketJson) {
                    String ticket = ticketJson.getString("ticket");
                    barCodeUrl = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + ticket;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    return barCodeUrl;
}

自定义菜单与开发者模式

二者不可兼容,如果想兼容,则

1.保持开发者模式开启(用于扫码关注回调,此时微信自定义菜单会失效)
2.用第三方托管自定义菜单,在第三方里设置菜单url

微信退款

需要再商户平台API安全中下载证书,放在服务器根目录下

https://www.sohu.com/a/234912109_100142931

⬆️