开发之微信、支付宝支付

这是一篇关于微信支付V2版(包含JSAPI支付、Native支付和H5支付)、支付宝支付的文章,微信不知道什么时候更新了一个V3版,加密型更严格了。关于微信支付和支付宝商家的注册,本文就不说了,不了解的可以去某度一下,提前说一下微信支付必须是先注册服务号,微信支付和服务号进行绑定才能正常在网上支付。
完整项目地址: https://github.com/Q631124/qi-pay

这里先说一下微信支付,开发文档地址:https://pay.weixin.qq.com/wiki/doc/api/index.html,我们这里有一个支付页面:

支付页面.png
//    去支付的js代码:confirm.js

function toPay() {

var gg=true;

if (gg) {

var payway =$('input[name=pay-way]:checked').val();

var phone =$('input[name=phone]').val();

var isPC =1;//默认为电脑支付

        if ($('.pay-way input:checked').length <1) {

alert("请选择付款方式");

gg=false;

}else if (payway ==0) {

//电脑端微信支付

            if(is_pc()){

$("#frm").attr("action","/page/payorder.do?pay_type=pc");//微信支付

                gg=true;

}else{

if(is_weixin()){

//手机端微信支付

                    gg =false;

//appid为服务号中的APPID

                    window.location.href ="https://open.weixin.qq.com/connect/oauth2/authorize?appid=xxxxxxxxx&redirect_uri="+encodeURIComponent('http://127.0.0.1/pay/getWxCode.do?pay_type=wx&phone='+phone)+"&response_type=code&scope=snsapi_base&state=123#wechat_redirect";

}else{

//手机端h5支付

                    gg=true;

$("#frm").attr("action","/pay/getWxCode.do?pay_type=h5&code=1");

}

}

}else if (payway ==1) {

//支付宝支付判断是否在微信内部

            if(is_weixin()){

gg =false;

alert("请点击右上角用手机浏览器打开该网页");

}else{

$("#frm").attr("action","/pay/alipay.do");

gg=true;

}

}

}

return gg;

}

//是否为电脑支付,是返回true

function is_pc() {

var userAgentInfo =navigator.userAgent;

var Agents = ["Android","iPhone",

"SymbianOS","Windows Phone",

"iPad","iPod"];

var flag =true;

for (var v =0;v

if (userAgentInfo.indexOf(Agents[v]) >0) {

flag =false;

break;

}

}

return flag;

}

//判断是否是微信环境

function is_weixin() {

var ua =navigator.userAgent.toLowerCase();

var isWeixin =ua.indexOf('micromessenger') != -1;

if (isWeixin) {

return true;

}else{

return false;

}

}
//PayAction.java

/**

* 获取微信支付链接

* @return

*/

@ResponseBody

@RequestMapping(value = {"/wechat"})

public Map getWeChat(User user, String code, String pay_type, HttpServletRequest request, HttpServletResponse response){

String code_url ="";

Map returnMap =new HashMap();

try {

String phone = user.getPhone();

//创建订单号

        String out_trade_no = IDGeneratorUtil.getID("DD");

request.getSession().setAttribute("ORDER_NUM", out_trade_no);

request.getSession().setAttribute("phone", phone);

request.getSession().setAttribute("orderNum", out_trade_no);

String body = ClassItemUtil.getClassName(user.getClassItem());

int price = (int)(user.getPrice() *100);//价格,单位为分

        String classItem = user.getClassItem();

String name = user.getName();

// 账号信息

        String appid = PayConfigUtil.APP_ID;// appid

        String mch_id = PayConfigUtil.MCH_ID;// 商业号

        String key = PayConfigUtil.API_KEY;// key

        String currTime = PayToolUtil.getCurrTime();

String timeStamp = PayToolUtil.getTimeStamp();

String nonce_str = UUID.randomUUID().toString().replace("-","").substring(0,16);

SortedMap packageParams =new TreeMap();

// 终端IP

        String spbill_create_ip = getIpAddr(request);

// 回调接口

        String notify_url = PayConfigUtil.NOTIFY_URL;

String trade_type ="";

String openId ="";

String scene_info ="";

if("pc".equals(pay_type)){

trade_type = PayConfigUtil.TRADE_TYPE_NATIVE;

packageParams.put("product_id",currTime);

}else if("wx".equals(pay_type)){

trade_type = PayConfigUtil.TRADE_TYPE_JSAPI;

//页面获取openId接口

            String getopenid_url ="https://api.weixin.qq.com/sns/oauth2/access_token";

String  param="appid="+appid+"&secret=xxxxxxxxx"+"&code="+code+"&grant_type=authorization_code";

//向微信服务器发送get请求获取openIdStr

            String openIdStr = HttpRequest.sendGet(getopenid_url, param);

JSONObject json = JSONObject.parseObject(openIdStr);//转成Json格式

            openId = json.getString("openid");//获取openId

            packageParams.put("openid",openId);

}else if("h5".equals(pay_type)){

trade_type = PayConfigUtil.TRADE_TYPE_H5;

scene_info ="{\"h5_info\":{\"type\": \"Wap\",\"wap_url\": \"http://127.0.0.1/page/confirm.do\",\"wap_name\": \"水果大甩卖\"}}";

packageParams.put("scene_info",scene_info);

}

String params = out_trade_no +"," + phone +"," + name +"," + classItem +"," + user.getPrice();

String attach = java.net.URLEncoder.encode(params,"UTF-8");

packageParams.put("appid", appid);

packageParams.put("mch_id", mch_id);

packageParams.put("nonce_str", nonce_str);

packageParams.put("body", body);//(调整为自己的名称)

        packageParams.put("out_trade_no", out_trade_no);

packageParams.put("total_fee", price);//价格的单位为分

        packageParams.put("spbill_create_ip", spbill_create_ip);

packageParams.put("notify_url", notify_url);

packageParams.put("trade_type", trade_type);

packageParams.put("attach",attach);

packageParams.put("sign_type","MD5");

String sign = PayToolUtil.createSign("UTF-8", packageParams,key);

packageParams.put("sign", sign);

String requestXML = PayToolUtil.getRequestXml(packageParams);

String resXml = HttpUtil.postData(PayConfigUtil.UFDODER_URL, requestXML);

Map map = XMLUtil4jdom.doXMLParse(resXml);

String return_code = (String)map.get("return_code");

String return_msg = (String)map.get("return_msg");

String prepay_id ="";

String mweb_url ="";

if("SUCCESS".equals(return_code)){

code_url = (String) map.get("code_url");

if("wx".equals(pay_type)){

prepay_id = (String) map.get("prepay_id");

}else if("h5".equals(pay_type) &&"OK".equals(return_msg)){

mweb_url = (String) map.get("mweb_url");//调微信支付接口地址

                String redirect_url ="http://127.0.0.1/";

String redirect_urlEncode =  URLEncoder.encode(redirect_url,"utf-8");//对上面地址urlencode

                mweb_url = mweb_url +"&redirect_url=" + redirect_urlEncode;

returnMap.put("mweb_url",mweb_url);

}

}else {

throw new Exception("返回失败");

}

if("wx".equals(pay_type)){

returnMap.put("appId",appid);

returnMap.put("timeStamp",timeStamp);

returnMap.put("nonceStr",nonce_str);

returnMap.put("package","prepay_id=" + prepay_id);

returnMap.put("signType","MD5");

SortedMap returnParams =new TreeMap();

returnParams.put("appId",appid);

returnParams.put("timeStamp",timeStamp);

returnParams.put("nonceStr",nonce_str);

returnParams.put("package","prepay_id=" + prepay_id);

returnParams.put("signType","MD5");

String paySign = PayToolUtil.createSign("UTF-8", returnParams,key);

returnMap.put("paySign",paySign);

}

returnMap.put("pay_type",pay_type);

returnMap.put("code_url",code_url);

returnMap.put("out_trade_no", out_trade_no);

}catch (Exception e){

e.printStackTrace();

}

return returnMap;

}

PC端支付,生成二维码扫码支付,请求/pay/wechat.do返回支付连接,使用/pay/createQRCode.do生成二维码


$.ajax({

url:'/pay/wechat.do',

type:'post',

data: {

phone:phone,

pay_type:pay_type,

name:name,

phone:phone,

price:price,

classItem:classItem

    },

async:false,

dataType:'JSON',

success:function (data) {

if(data !=null){

var code_url = data.code_url;

if(code_url !=null){

$('#wx_pay_qrcode img').attr("src","/pay/createQRCode.do?code_url="+code_url);

var interval=setInterval(function(){

$.ajax({

url:'/pay/paystate.do',

type:'post',

data:{

out_trade_no: data.out_trade_no

                        },

success:function(dt){

if(dt=="ok"){

clearInterval(interval);

window.location.href ="http://127.0.0.1/page/paysuccess.do";

}

}

});

},3000);

}

}

}

});

这里需要导入jar包:Qrcode-1.0.jar,详情见文章末尾连接


@RequestMapping({"/createQRCode"})

public void createQRCode(String code_url,HttpServletResponse response) {

try {

Qrcode handler =new Qrcode();

handler.setQrcodeErrorCorrect('M');

handler.setQrcodeEncodeMode('B');

handler.setQrcodeVersion(7);

byte[] contentBytes = code_url.getBytes("UTF-8");

int imgSize =280;

BufferedImage bufImg =new BufferedImage(imgSize, imgSize,1);

Graphics2D gs = bufImg.createGraphics();

gs.setBackground(Color.WHITE);

gs.clearRect(0,0, imgSize, imgSize);

gs.setColor(Color.BLACK);

int pixoff =2;

if ((contentBytes.length >0) && (contentBytes.length <800)) {

boolean[][] codeOut = handler.calQrcode(contentBytes);

for (int i =0; i < codeOut.length; i++) {

for (int j =0; j < codeOut.length; j++) {

if (codeOut[j][i]) {

gs.fillRect(j *6 + pixoff, i *6 + pixoff,6,6);

}

}

}

}

gs.dispose();

bufImg.flush();

ImageIO.write(bufImg,"jpg", response.getOutputStream());

}catch (Exception e) {

e.printStackTrace();

}

}

微信内和手机H5页面请求/pay/getWxCode.do,到wxcode.jsp文件中调用js


$.ajax({

url:'/pay/wechat.do',

type:'post',

data: {

phone:phone,

pay_type:pay_type,

code:code,

name:name,

phone:phone,

price:price,

classItem:classItem

    },

dataType:'JSON',

success:function (data) {

if(data !=null){

if(data.pay_type =="wx"){

if (typeof WeixinJSBridge =="undefined"){

if(document.addEventListener ){

document.addEventListener('WeixinJSBridgeReady',onBridgeReady,false);

}else if (document.attachEvent){

document.attachEvent('WeixinJSBridgeReady',onBridgeReady);

document.attachEvent('onWeixinJSBridgeReady',onBridgeReady);

}

}else{

onBridgeReady();

}

}else if(data.pay_type =="h5"){

window.location.href = data.mweb_url;

}

function onBridgeReady() {

WeixinJSBridge.invoke(

'getBrandWCPayRequest', {

"appId": data.appId,//公众号名称,由商户传入

                        "timeStamp": data.timeStamp,//时间戳

                        "nonceStr": data.nonceStr,//随机串

                        "package": data.package,

"signType":"MD5",//微信签名方式:

                        "paySign": data.paySign //微信签名

                    },

function (res) {

if (res.err_msg =="get_brand_wcpay_request:ok") {

window.location.href ="/page/paysuccess.do";

}else{

alert("支付失败");

}

});

}

}

}

});

微信支付调用成功后都会返回通知页面,在页面上请求判断是否支付成功,在result_code返回SUCCESS是添加自己的业务逻辑


/**

* 微信支付成功返回

* @param request

* @param response

*/

@RequestMapping(value="/wxSuccess")

public void wxSuccess(HttpServletRequest request, HttpServletResponse response) {

String[] params =null;

BufferedReader in =null;

InputStream inputStream =null;

BufferedOutputStream out =null;

try{

//读取参数

        StringBuffer sb =new StringBuffer();

inputStream = request.getInputStream();

String s ="";

in =new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));

while ((s = in.readLine()) !=null){

sb.append(s);

}

in.close();

inputStream.close();

//解析xml成map

        Map map =new HashMap();

map = XMLUtil4jdom.doXMLParse(sb.toString());

//过滤空 设置 TreeMap

        SortedMap packageParams =new TreeMap();

Iterator it = map.keySet().iterator();

while (it.hasNext()) {

String parameter = (String) it.next();

String parameterValue = map.get(parameter);

String value ="";

if(null != parameterValue) {

value = parameterValue.trim();

}

packageParams.put(parameter, value);

}

// 账号信息

        String key = PayConfigUtil.API_KEY;//key

//判断签名是否正确

        if(PayToolUtil.isTenpaySign("UTF-8", packageParams,key)) {

String resXml ="";

if("SUCCESS".equals((String)packageParams.get("result_code"))){

String attach = (String)packageParams.get("attach");

if(!"".equals(attach) && attach !=null){

attach = java.net.URLDecoder.decode(attach,"UTF-8");

params = attach.split(",");

}

//自己的业务逻辑

//通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.

                resXml ="<xml>" +""

                        +"<return_msg><![CDATA[OK]]></return_msg>" +"</xml> ";

}else {

resXml ="<xml>" +""

                        +"<return_msg><![CDATA[报文为空]]></return_msg>" +"</xml> ";

}

out =new BufferedOutputStream(response.getOutputStream());

out.write(resXml.getBytes());

out.flush();

out.close();

}else{

logger.info("通知签名验证失败");

}

}catch (Exception e) {

e.printStackTrace();

}finally {

if(out !=null){

try {

out.close();

}catch (IOException e) {

e.printStackTrace();

}

}

if(inputStream !=null){

try {

inputStream.close();

}catch (IOException e) {

e.printStackTrace();

}

}

if(in !=null){

try {

in.close();

}catch (IOException e) {

e.printStackTrace();

}

}

}

}


以下为支付宝支付流程,支付宝支付相对于微信支付是简单的,支付宝在PC端会调用自己的支付页面,无需自己生成二维码,在手机端(微信内部除外)会自动调起支付宝客户端进行支付,支付宝开发文档地址:https://open.alipay.com/developmentDocument.htm


//支付宝支付,需要正确的参数才能调用成功

@RequestMapping(value={"/alipay"})

public void createQRCode(User user, HttpServletRequest request, HttpServletResponse response) {

try {

String orderNum = IDGeneratorUtil.getID("ZX");

request.getSession().setAttribute("ORDER_NUM", orderNum);//订单编号

        request.getSession().setAttribute("phone", user.getPhone());

request.getSession().setAttribute("orderNum", orderNum);

String subject = user.getClassItem();

float price = user.getPrice();

String params = orderNum +"," + user.getPhone() +"," + user.getName() +"," + user.getClassItem() +"," + price;

//支付携带参数,会原样返回

        String passback_params = java.net.URLEncoder.encode(params,"UTF-8");

AlipayClient alipayClient =new DefaultAlipayClient("https://openapi.alipay.com/gateway.do",

"xxxxxxxx",

"xxxxxxxxxxxxxxxxxxxxxx",

"json","utf-8",

"xxxxxxxxxxxxxxxxx",

"RSA2");

AlipayTradePagePayRequest alipayRequest =new AlipayTradePagePayRequest();

//通知地址

        alipayRequest.setReturnUrl("http://127.0.0.1/");

//支付成功后调用地址,支付成功后的业务要写在该逻辑中

        alipayRequest.setNotifyUrl("https://127.0.0.1/page/success.do");

alipayRequest.setBizContent("{    \"out_trade_no\":\"" + orderNum +"\"," +

"    \"product_code\":\"FAST_INSTANT_TRADE_PAY\"," +"    \"total_amount\":\"" + price +"\"," +

"    \"subject\":\"" + subject +"\"," +"    \"body\":\"" + subject +"\"," +

"    \"passback_params\":\" " + passback_params +" \"," +

"    \"extend_params\":{" +"    \"sys_service_provider_id\":\"xxxxxxxx\"" +"    }," +

"    \"timeout_express\":\"30m\"" +"  }");

String form = ((AlipayTradePagePayResponse)alipayClient.pageExecute(alipayRequest)).getBody();

response.setContentType("text/html;charset=utf-8");

response.getWriter().write(form);

response.getWriter().flush();

response.getWriter().close();

}catch (Exception e) {

e.printStackTrace();

}

}

支付宝接口文档中有两个参数:return_url、notify_url,区别是return_url为网页重定向通知,是用户付款成功后立刻返回的页面,主要是为了让用户看到自己已经支付成功了,支付宝不能保证其到达率,如果把支付成功的业务逻辑写到该页面下,不能保证业务逻辑正常执行;notify_url是支付宝服务器通知,在支付成功后支付宝进行的服务器请求,如果请求不成功,会在一定时间内陆续请求八次,提高了通知到达率,可以保证业务逻辑的执行。请求成功后必须以流的方式返回success或者fail,不返回支付宝会默认请求失败,会继续发起请求


/**

*  支付宝支付成功返回

* @param request

* @param response

*/

@RequestMapping("/success")

public void success(HttpServletRequest request, HttpServletResponse response) {

String passback_params = request.getParameter("passback_params");

String[] params =null;

try {

if(!"".equals(passback_params) && passback_params !=null){

passback_params = java.net.URLDecoder.decode(passback_params,"UTF-8");

params = passback_params.split(",");

}

addUser(params,request);

response.getWriter().print("success");

}catch (Exception e) {

e.printStackTrace();

try {

response.getWriter().print("fail");

}catch (IOException e1) {

e1.printStackTrace();

}

}

}

上面的内容还相对较乱,如果看的不是很明白,请访问github地址:https://github.com/Q631124/qi-pay里面有完整的代码逻辑。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,490评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,581评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,830评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,957评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,974评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,754评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,464评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,847评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,995评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,137评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,819评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,482评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,023评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,149评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,409评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,086评论 2 355