您的位置:首页 > 编程语言 > Java开发

springboot微信sdk方式进行微信支付

2018-05-16 21:13 393 查看
微信支付:
官方文档:https://pay.weixin.qq.com/wiki/doc/api/index.html
第三方SDK:https://github.com/Pay-Group/best-pay-sdk

首先说明一下,微信支付功能接口权限只有微信服务号才拥有。

首先来看一下公众号支付的业务流程图。

支付开发中,一般在第10步异步通知成功了,就基本上可以百分百说明支付成功了,可以修改支付状态为已支付。

开发步骤:

1.pom.xml中添加Maven依赖

<!-- 微信公众号支付依赖 -->

<dependency>
    <groupId>cn.springboot</groupId>
    <artifactId>best-pay-sdk</artifactId>
    <version>1.1.0</version>

</dependency>

2.微信服务号信息配置

package com.wechat.order.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/*微信公众号账号设置*/
@Data//使用了lombok依赖,相当于在代码中getter和setter的作用
@Component
@ConfigurationProperties(prefix = "wechat")//账号信息写在了application.yml中。
public class WeChatAccountConfig {

private String mpAppId;

private String mpAppSecret;

/**
* 商户号
*/
private String mchId;

/**
* 商户密钥
*/
private String mchKey;

/**
* 商户证书路径
*/
private String keyPath;

/**
* 异步通知路径
*/
private String notifyUrl;
}
package com.wechat.order.config;

import com.lly835.bestpay.config.WxPayH5Config;
import com.lly835.bestpay.service.impl.BestPayServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

/*微信公众账号支付配置*/
@Component
public class WeChatPayConfig {

@Autowired
private WeChatAccountConfig weChatAccountConfig;

//把你要实例化的对象转化成一个Bean,放在IoC容器中
@Bean
public BestPayServiceImpl bestPayService() {

//支付类, 所有方法都在这个类里
BestPayServiceImpl bestPayService = new BestPayServiceImpl();
bestPayService.setWxPayH5Config(wxPayH5Config());
return bestPayService;
}

@Bean
private WxPayH5Config wxPayH5Config() {
WxPayH5Config wxPayH5Config = new WxPayH5Config();
wxPayH5Config.setAppId(weChatAccountConfig.getMpAppId());//设置微信公众号的appid
wxPayH5Config.setAppSecret(weChatAccountConfig.getMpAppSecret());// 设置微信公众号的app corpSecret
wxPayH5Config.setMchId(weChatAccountConfig.getMchId());// 设置商户号
wxPayH5Config.setMchKey(weChatAccountConfig.getMchKey());// 设置商户密钥
wxPayH5Config.setKeyPath(weChatAccountConfig.getKeyPath());// 设置商户证书路径
wxPayH5Config.setNotifyUrl(weChatAccountConfig.getNotifyUrl());// 设置支付后异步通知url
return wxPayH5Config;
}
}
3.调用方法发起支付

controller

/**
* 微信支付发起
* @param orderId
* @param returnUrl
* @return
*/
@RequestMapping("/create")
public ModelAndView create(@RequestParam("orderId")String orderId,
@RequestParam("returnUrl")String returnUrl,
HashMap<String, Object> map){

//根据订单id查询订单,数据库查询
OrderDTO orderDTO = orderService.findOne(orderId);
if(orderDTO == null){//订单不存在抛出错误
throw new SellException(ResultEnum.ORDER_NOT_EXIST);
}

//微信支付
PayResponse payResponse = wechatPayService.create(orderDTO);

//携带参数传入网页发起支付的前端模板
map.put("orderId", orderId);
map.put("payResponse", payResponse);

return new ModelAndView("pay/create",map);
}
service:
//微信支付发起
public PayResponse create(OrderDTO orderDTO) {
PayRequest payRequest = new PayRequest();
payRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);//支付方式,微信公众账号支付
payRequest.setOrderId(orderDTO.getOrderId());//订单号.
payRequest.setOrderName(payName);//订单名字.
payRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue());//订单金额.
payRequest.setOpenid(orderDTO.getBuyerOpenid());//微信openid, 仅微信支付时需要
log.info("【微信支付】request={}", JsonUtil.toJson(payRequest));//将payRequest格式化一下,再显示在日志上,便于观看数据
PayResponse payResponse = bestPayService.pay(payRequest);
log.info("【微信支付】response={}", JsonUtil.toJson(payResponse));//将payResponse格式化一下,再显示在日志上,便于观看响应后返回的数据);
return payResponse;
}
JsonUtil格式转换工具类:
/**
* 把对象转换成json格式,便于观看
*/
public class JsonUtil {

public static String toJson(Object object) {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setPrettyPrinting();
Gson gson = gsonBuilder.create();
return gson.toJson(object);
}
}
4.在static文件夹下编写微信内H5调起支付的前端代码pay.html(可以从微信支付官方文档中拷贝代码),检测是否可以支付,此步骤可省略。
ps:为什么把支付的前端代码写在后台中?
如果放在前端的话,加入几个地方或几个模块都可以发起微信支付,每次发起都要调用这个前端代码,
这个前端代码就要写好几次。如果放在后端的话,只要给个请求,就可以直接调用这段代码。

5.对支付的前端代码动态注入参数以发起支付。
这里我们用到模板技术。

(1)在pom.xml中引入freemarker依赖

        <!-- freemarker模板技术 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>

(2)把controller中发起支付的方法返回类型改为ModelAndView,return new ModelAndView("path",model);//path为模板存放的路径,不包括模板后缀名。这里model我们采用Map类型,把payResponse和returnUrl携带过去。
(3)在相应的地方,创建模板文件xx.ftl。如我上述步骤的path为pay/create,则我在resources/templates目录下创建文件夹pay,在pay目录下再创建create.ftl文件。
(4)修改pay.html成模板,参数动态注入。${xxxx}

(5)模板中,支付后进行跳转到returnUrl。

create.ftl模板:

<script>
function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":"${payResponse.appId}",     //公众号名称,由商户传入
"timeStamp":"${payResponse.timeStamp}",         //时间戳,自1970年以来的秒数
"nonceStr":"${payResponse.nonceStr}", //随机串
"package":"${payResponse.packAge}",
"signType":"MD5",         //微信签名方式:
"paySign":"${payResponse.paySign}" //微信签名
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) {}     // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。
}
local.href="${returnUrl}";
);
}
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);

20000
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}
</script>
6.填写微信支付的支付授权目录。
比如我的前端支付代码访问路径是http://xxxxx(网站根路径)/sell/pay.html。
支付授权目录就写http://xxxxx(网站根路径)/sell/

7.微信异步通知,并在相应的controller中进行支付状态修改。
依靠前端返回的支付成功来判断成功并不安全,容易被篡改。必须依靠后端异步通知来判断是否已支付成功
(1)验证签名,支付状态
(2)校验支付金额与订单金额是否一致,2个金额相减小于0.01即认为相等。
(3)修改订单支付状态

service:

/*微信支付异步通知*/
@Override
public PayResponse notify(String notifyData) {
//1. 验证签名
//2. 支付的状态
//3. 支付金额
//4. 支付人(下单人 == 支付人)

PayResponse payResponse = bestPayService.asyncNotify(notifyData);
log.info("【微信支付】异步通知, payResponse={}", JsonUtil.toJson(payResponse));

//查询订单
OrderDTO orderDTO = orderService.findOne(payResponse.getOrderId());

//判断订单是否存在
if (orderDTO == null) {
log.error("【微信支付】异步通知, 订单不存在, orderId={}", payResponse.getOrderId());
throw new SellException(ResultEnum.ORDER_NOT_EXIST);
}

//判断金额是否一致(0.10   0.1)
if (!MathUtil.equals(payResponse.getOrderAmount(), orderDTO.getOrderAmount().doubleValue())) {
log.error("【微信支付】异步通知, 订单金额不一致, orderId={}, 微信通知金额={}, 系统金额={}",
payResponse.getOrderId(),
payResponse.getOrderAmount(),
orderDTO.getOrderAmount());
throw new SellException(ResultEnum.WXPAY_NOTIFY_MONEY_VERIFY_ERROR);
}

//修改订单的支付状态
orderService.paid(orderDTO);

return payResponse;
}
controller:
/**
* 微信异步通知
* @param notifyData
*/
@PostMapping("/notify")
public ModelAndView notify(@RequestBody String notifyData) {
wechatPayService.notify(notifyData);

//返回给微信处理结果
return new ModelAndView("pay/success");
}
}
success.ftl异步通知成功模板:
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
</xml>

8.微信退款
(1)设置退款人,退款金额,退款方式
(2)退款

service:
/**
* 退款
* @param orderDTO
*/
@Override
public RefundResponse refund(OrderDTO orderDTO) {
RefundRequest refundRequest = new RefundRequest();
refundRequest.setOrderId(orderDTO.getOrderId());
refundRequest.setOrderAmount(orderDTO.getOrderAmount().doubleValue());
refundRequest.setPayTypeEnum(BestPayTypeEnum.WXPAY_H5);
log.info("【微信退款】request={}", JsonUtil.toJson(refundRequest));

RefundResponse refundResponse = bestPayService.refund(refundRequest);
log.info("【微信退款】response={}", JsonUtil.toJson(refundResponse));

return refundResponse;
}
}


阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: