Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

发起支付

关键类:net.mingsoft.pay.action.web.PayAction.java 关键方法:gateway(....)

HTTP发送接口

HTTP方法:POST

请求URL:/mpay/pay/gateway.do

请求参数:

参数是否必选类型可选值范围默认值说明
typeStringweixin、alipay支付类型
orderPrice价格
orderNoString订单号
orderNameString订单名称
orderDesc商品描述
notifyUrl可选String/mpay/alipay/notify.do /mpay/weixin/notify.do会根据 type 的值, 自动调用对应类型
attachJSON自定义业务参数{'notifyBeanName':'回调方法','userId':'用户ID,方便记录log日志'},notifyBeanName和userId参数一定要有,其余按需扩展
returnUrlString只支持支付宝,支付成功结果页面,必须是http绝对路径
showTypeStringqrCode或其他影响支付宝二维码展示方式,值为qrCode时返回二维码链接,否则返回支付宝新窗口页面;微信始终返回二维码链接

接口响应示例:

showType为qrCode:

{
  "result" : true,
  "code" : 200,
  "data" : {
    "orderNo" : "17780286754386925",
    "qrCode" : "https://qr.alipay.com/bax08719wyrmbq9l1xxxxxxx", //支付宝返回二维码链接
    "qrCode" : "weixin://wxpay/bizpayurl?pr=xxxxxxxxx", // 微信返回二维码链接
    "orderPrice" : "0.99", // 实际支付价格
    "rawPrice" : "1" // 原价 仅展示使用
  }
}

showType为其他值(仅支付宝):

{
  "result": true,
  "code": 200,
  "data": "<form name=\"punchout_form\" method=\"post\" action=\"https://openapi.alipay.com/gateway.do?charset=UTF-8&method=alipay.trade.page.pay&sign=DEMO_SIGNATURE_STRING&return_url=https%3A%2F%2Fexample.com%2Freturn%3Fmsg%3Dsuccess&notify_url=https%3A%2F%2Fexample.com%2Fnotify%2Falipay&version=1.0&app_id=DEMO_APP_ID_2021000000000000&sign_type=RSA2&timestamp=2026-01-01+00%3A00%3A00&alipay_sdk=alipay-sdk-java-demo&format=json\">\n<input type=\"hidden\" name=\"biz_content\" value=\"{&quot;body&quot;:&quot;演示商品描述&quot;,&quot;out_trade_no&quot;:&quot;DEMO_ORDER_NO_202601010001&quot;,&quot;product_code&quot;:&quot;FAST_INSTANT_TRADE_PAY&quot;,&quot;subject&quot;:&quot;演示订单&quot;,&quot;timeout_express&quot;:&quot;30m&quot;,&quot;total_amount&quot;:&quot;0.01&quot;}\">\n<input type=\"submit\" value=\"立即支付\" style=\"display:none\" >\n</form>\n<script>document.forms[0].submit();</script>"
}

支付回调

默认回调地址

默认回调可以完成基础订单支付

支付宝回调

/mpay/alipay/notify.do

微信回调

/mpay/weixin/notify.do

扩展回调

大多数时候需要根据回调状态更新业务数据,通过 attach 参数中的 notifyBeanName 设置对应 springbean 名称实现

定义 class 实现 IPayNotify 接口

     /**
     * 支付时候,定义的attach的参数值
     * @param payLogEntity 支付的日志信息
     * @param attach {notifyBeanName:'业务扩展的bean,经过spring管理','userId':'用户Id',自定义参数:值}
     * @return
     */
@Service("diyPayNotify")
public class PayNotify implements IPayNotify {
    @Override
    public boolean notify(PayLogEntity payLog, HashMap attach) {
        //订单号
        //payLog.getOrderNo();
    }
}

范例

Tip

  1. 必须确保已经配置好了支付宝或者微信的相关配置;
  2. 调用mpay/pay/gateway方法时,必须遵循请求参数规范
  3. 本地开发时,回调地址(notifyUrl)需要填写内网穿透后的地址。

支付宝支付

首先编写一个简单表单页面 "alipay.html"

<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
...
<form>
    <input type="text" name="returnUrl" value="http://localhost:8080"/><!--支付成功返回地址-->
    <input type="text" name="orderNo" value="88888888"/><!--订单号 不填写系统会自动生成-->
    <input type="text" name="orderName" value="铭飞开源赞助"/><!--订单名称-->
    <!-- <input type="text" name="notifyUrl" value="自定义回调地址"/> --><!--回调地址,如果不填写默认由type决定调用当前系统的回调方法-->
    <input type="text" name="type" value="alipay"/><!--支付类型-->
    <input type="text" name="orderPrice" value="0.01"/><!--价格-->
    <input type="text" name="orderDesc" value="价值源自分享"/><!--商品描述-->
    <input type="text" name="attach" value="{'notifyBeanName':'diyPayNotify','userId':'用户Id'}"/><!--需要自定义扩展回调方法-->
    
</form>
...

<script>
    $.ajax({
        type: "POST",
        dataType:"json",
        url: "http://localhost:8080/mpay/pay/gateway.do",
        data: $("form").serialize(),
        success: function(res){
           if(res.result) {
               document.write(res.data);
           }
        }
    });
</script>

微信扫码支付

编写一个简单的表单页,根据表单提交的结果显示二维码图片,通常结合ajax来实现

<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery.qrcode/1.0/jquery.qrcode.min.js"></script>
...
<form>
    <input type="hidden" name="orderNo" value="8"/><!--订单号-->
    <input type="hidden" name="type" value="weixin"/><!--支付类型-->
    <input type="hidden" name="orderPrice" value="0.01"/><!--价格-->
    <input type="hidden" name="orderDesc" value="价值源自分享"/><!--商品描述-->
    <!--<input type="text" name="notifyUrl" value="自定义回调地址"/>--><!--回调地址,如果不填写默认为当前系统-->
    <input type="text" name="attach" value="{'notifyBeanName':'diyPayNotify','userId':'用户Id'}"/><!--需要自定义扩展回调方法-->

</form>
<!--显示二维码-->
<div id="qrcode"></div>
...
<script>
    $.ajax({
        type: "POST",
        dataType:"json",
        url: "http://localhost:8080/mpay/pay/gateway.do",
        data: $("form").serialize(),
        success: function(res){
            if(res.result) {
                $("#qrcode").qrcode(res.data);
            } else {
                alert('重新修改支付参数');
            }

        }
    });
</script>


微信公众号支付

微信公众号支付需要在公众号内部完成,同时用户需要进行授权登录,注意在前往授权登录的js中,请使用示例中 location.href 赋值的方式,使用window.open()的方式会遇到被拦截的情况

参考微信官方文档

<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<!--创建支付表单页面-->
<form id="weixin" name="weixin" >
    <input type="hidden" name="orderNo" value="888888888"/><!--订单编号-->
    <input type="hidden" name="type" value="weixin"/><!--支付类型-->
    <input type="hidden" name="orderPrice" value="0.01"/><!--价格-->
    <input type="hidden" name="orderDesc" value="测试公众号支付"/><!--描述-->
    <input type="hidden" name="page" value="http://项目地址/wx-mp-pay.jsp"/><!--接收微信支付授权信息的页面-->
    <!--多个公众号支付必传-->
    <input type="hidden" name="appId" value="xxxxxxxx"/><!--微信appId-->
    <input type="hidden" name="mchId" value="1234567890"/><!--微信支付商户号-->
    <input type="hidden" name="key" value="xxxxxxxx"/><!--微信支付API密钥-->
    <input type="hidden" name="secret" value="xxxxxxxx"/><!--Appsecret-->
    <input type="submit" value="确认">
</form>
...
<script>
//授权登录,需要用户配置微信appid和项目请求支付地址
function weixinPay(){
    location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=微信appid&redirect_uri=" + encodeURIComponent("http://项目地址/mpay/pay/gateway.do?" + $("#weixin").serialize()) + "&response_type=code&scope=snsapi_userinfo#wechat_redirect";
}
</script>

确认支付

<!--确认支付页面-->
<script>
function pay() {
    WeixinJSBridge.invoke(
               'getBrandWCPayRequest',  {
                   "appId":<%=request.getParameter("wxAppId")%>,     //公众号名称,由商户传入
                   "timeStamp":"<%=request.getParameter("timeStamp")%>",         //时间戳,自1970年以来的秒数
                   "nonceStr":"<%=request.getParameter("nonceStr")%>", //随机串
                   "package":"<%=request.getParameter("package")%>",
                   "signType":"MD5",         //微信签名方式:
                   "paySign":"<%=request.getParameter("sign")%>" //微信签名
              },
              function(res){
                   if (res.err_msg == "get_brand_wcpay_request:ok") {
                      alert("微信支付成功!");
                  } else if (res.err_msg == "get_brand_wcpay_request:cancel") {
                      alert("用户取消支付!");
                  } else {
                      alert(JSON.stringify(res));
                  }
               }
    );
}
</script>

微信刷卡支付

刷卡支付场景描述:用户出示微信付款二维码 、 商户使用扫码枪扫码,用户输入支付密码(小额免密),商务系统提示支付成功。

开发者需要制作一张支付表单页面,提交后直接返回支付结果

<!-- 微信刷卡支付,刷卡成功后会直接扣款,金额过大或其他原因会需要支付密码 -->
<!-- 具体参考微信官方文档:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=5_1 -->
<form id="submit" name="weixin" action="http://域名/项目/mpay/pay/gateway.do" method="post">
    <input type="hidden" name="orderNo" value="888888"/><!--订单编号-->
    <input type="hidden" name="type" value="weixin"/><!--支付类型-->
    <input type="hidden" name="orderPrice" value="0.01"/>
    <input type="hidden" name="orderDesc" value="铭飞商超系统"/>
    <input type="text" name="authCode" value="45454XXXX3FF"/> <!--通过扫描枪扫码获取,可手动输入付款码底部对应的字符串-->
    <input type="submit" value="确认">
</form>

通用支付范例

通用支付页面,在服务器端封装页面需要的参数并渲染到支付页面,提高安全性

前端自定义页面模板

    ...
    <!--二维码-->
    <div> 
      <qriously :value="wxCode" v-if="wxCode" :size="200"/>
    </div>
    <span class="pay-price">${pay.price}</span>
    ...
script
    data: {
        wxCode: "${pay.wxCode!''}",// 微信二维码
        orderNo: "${pay.orderNo}",
    
    },
    methods:{
        //	查询验证订单状态
        checkOrder: function() {
            var that = this
            ms.http.get('/mpay/pay/status.do?orderNo=${pay.orderNo}').then(function(res) {
                if(res.result && res.data == 'PAY') {
                    that.orderStatus = true;
                    clearInterval(that.listenerTimer)
                }
            })
        },
    }

后端 封装页面需要的参数

// 业务对接支付接口
    // 组装PayBean
    PayBean pay = new PayBean();
    pay.setOrderPrice("price"); // 价格
    pay.setOrderNo("orderNo"); // 订单号
    pay.setOrderName("orderName"); // 订单名称
    pay.setOrderDesc("orderDesc"); // 订单描述
    // xxxNotify需要实现IPayNotify接口 实现回调业务 params 自定义参数  具体查看本章扩展回调
    pay.setAttach("{'notifyBeanName':'xxxNotify','params':"+params+"}");
    pay.setReturnUrl(BasicUtil.getUrl()+"renwudating.html");
    // payType 为空时默认微信支付
    pay.setType(PayBean.Type.WEIXIN);

    //微信支付,返回支付二维码
    String wxCode = HttpUtil.post(BasicUtil.getUrl() + "/mpay/pay/gateway.do", BeanUtil.beanToMap(pay));
    ResultData resultData = JSONUtil.toBean(wxCode, ResultData.class);
    Map<String, Object> params = BasicUtil.assemblyRequestMap();
    params.put("qrCode",resultData.getData(String.class));
    // ... 其他需要在页面上渲染的参数 
    request.setAttribute("pay",params);
    // 转发到自定义页面请求 
    return "forward:/xxx.do"


发起退款

业务方法关键类

net.mingsoft.pay.biz.IPayLogBiz 关键方法:refund(....)

范例1

PayRefundBean payRefundBean = new PayRefundBean();
payRefundBean.setOrderNo("201908080808"); //订单号
payRefundBean.setType("WEI_XIN"); //支付宝支付:ALI_PAY 微信支付:WEI_XIN
payRefundBean.setPrice("8.8"); //退款费用
payRefundBean.setServiceCharge(0.5); //手续费
if(!net.mingsoft.pay.biz.IPayLogBiz.refund(payRefundBean){
    System.out.print("退款失败");   
} else {
    System.out.print("退款成功");
    // 流水交易为已退款
    UpdateWrapper<PayLogEntity> wrapper = new UpdateWrapper<>();
    wrapper.lambda().eq(PayLogEntity::getOrderNo,rewardedTask.getOrderNo()).set(PayLogEntity::getLogStatus,PayLogEntity.LogStatusEnum.REFUND);
    payLogBiz.update(wrapper);
    // 订单记录为已退款
    ···
}

注意在具体业务调用时退款同时要将订单状态和流水状态修改为已退款

范例2

除了bean形参的方法,还提供了一个重载方法形参为流水id范例

···
if(!net.mingsoft.pay.biz.IPayLogBiz.refund(payLogEntity.getId())){
    System.out.print("退款失败");   
} else {
    System.out.print("退款成功");
    // 订单记录为已退款
    ···
}