java版+支付宝支付和微信支付(一)

java版+支付宝支付和微信支付(一)

2016年12月29日 15:36:39
  • 15193

最近公司在做支付模块,在接入过程中遇到了很多坑,费了不少事,现在分享一下接入方法,也记录一下,以后可能还用的到。用的是支付宝的即时到帐支付功能和微信的扫码支付功能,相比起来,个人感觉支付宝的文档和接入方式都比微信的容易理解和操作,也不用自己写页面,接入起来比较方便,毕竟是支付起家的,比微信支付少很多坑,下面就分别介绍着两种支付的接入方法。

支付宝支付

1、申请签约

目的是得到开发使用的合作伙伴身份(PID)和MD5秘钥,申请地址(即时到账收款):https://b.alipay.com/order/productDetail.htm?productId=2015110218012942


申请方式在开放平台的文档上有详细说明,这里就不再赘述。

2、接入支付接口

在得到PID和秘钥后就可以接入接口了,首先在开放平台中下载官方的demo(java+MD5版本),支付宝的demo做的非常好,下载下来直接配置下jdk就可以运行了。如果遇到Java compiler level does not match错误,说明你用的eclipse或myeclipse的jdk编译版本与demo的JDK编译版本不一致,修改下jdk编译版本就可以了。其实就用到了4个类,如下图 
项目目录 
可以选择把支付功能单独做一个项目,在其他项目调用接口就可以支付,也可以整合到自己的项目里,为了好维护我整合到自己的项目里了。把这四个类放到自己的项目中,引入相应的jar包

2.1、demo中类的说明

AlipayConfig.java类主要是配置参数信息的类

package com.fahai.pay.alipay;

import com.fahai.utils.ProInfoUtil;

/* *
 *类名:AlipayConfig
 *功能:基础配置类
 *详细:设置帐户有关信息及返回路径
 *版本:3.4
 *修改日期:2016-03-08
 *说明:
 *以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
 *该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
 */

public class AlipayConfig {
//↓↓↓↓↓↓↓↓↓↓请在这里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
    // 合作身份者ID,签约账号,以2088开头由16位纯数字组成的字符串,查看地址:https://b.alipay.com/order/pidAndKey.htm
    public static String partner = "你自己的PID";
    // 收款支付宝账号,以2088开头由16位纯数字组成的字符串,一般情况下收款账号就是签约账号
    public static String seller_id = partner;
    // MD5密钥,安全检验码,由数字和字母组成的32位字符串,查看地址:https://b.alipay.com/order/pidAndKey.htm
    public static String key = "你自己的MD5秘钥";
    // 服务器异步通知页面路径  需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
//异步通知页面,就是接受支付宝支付结果返回信息的Controller,可以处理自己的支付后的逻辑
    //测试环境
    public static String notify_url = ProInfoUtil.getInstance().getProperty("project_url")+"order/pay/aliPayOrder";

    // 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
    //同步跳转的页面,就是支付宝支付成功后页面跳转的url
    public static String return_url = ProInfoUtil.getInstance().getProperty("project_url")+"order/pay/payResponse";

    // 签名方式
    public static String sign_type = "MD5";

    // 调试用,创建TXT日志文件夹路径,见AlipayCore.java类中的logResult(String sWord)打印方法。
    public static String log_path = "C:\\";

    // 字符编码格式 目前支持 gbk 或 utf-8
    public static String input_charset = "utf-8";

    // 支付类型 ,无需修改
    public static String payment_type = "1";

    // 调用的接口名,无需修改
    public static String service = "create_direct_pay_by_user";


//↑↑↑↑↑↑↑↑↑↑请在这里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

//↓↓↓↓↓↓↓↓↓↓ 请在这里配置防钓鱼信息,如果没开通防钓鱼功能,为空即可 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

    // 防钓鱼时间戳  若要使用请调用类文件submit中的query_timestamp函数
    public static String anti_phishing_key = "";

    // 客户端的IP地址 非局域网的外网IP地址,如:221.0.0.1
    public static String exter_invoke_ip = "";

//↑↑↑↑↑↑↑↑↑↑请在这里配置防钓鱼信息,如果没开通防钓鱼功能,为空即可 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

AlipayCore.java是整理参数的工具类

package com.alipay.util;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.httpclient.methods.multipart.FilePartSource;
import org.apache.commons.httpclient.methods.multipart.PartSource;

import com.alipay.config.AlipayConfig;

/* *
 *类名:AlipayFunction
 *功能:支付宝接口公用函数类
 *详细:该类是请求、通知返回两个文件所调用的公用函数核心处理文件,不需要修改
 *版本:3.3
 *日期:2012-08-14
 *说明:
 *以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
 *该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
 */

public class AlipayCore {

    /** 
     * 除去数组中的空值和签名参数
     * @param sArray 签名参数组
     * @return 去掉空值与签名参数后的新签名参数组
     */
    public static Map<String, String> paraFilter(Map<String, String> sArray) {

        Map<String, String> result = new HashMap<String, String>();

        if (sArray == null || sArray.size() <= 0) {
            return result;
        }

        for (String key : sArray.keySet()) {
            String value = sArray.get(key);
            if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
                || key.equalsIgnoreCase("sign_type")) {
                continue;
            }
            result.put(key, value);
        }

        return result;
    }

    /** 
     * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
     * @param params 需要排序并参与字符拼接的参数组
     * @return 拼接后字符串
     */
    public static String createLinkString(Map<String, String> params) {

        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);

        String prestr = "";

        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);

            if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符
                prestr = prestr + key + "=" + value;
            } else {
                prestr = prestr + key + "=" + value + "&";
            }
        }

        return prestr;
    }

    /** 
     * 写日志,方便测试(看网站需求,也可以改成把记录存入数据库)
     * @param sWord 要写入日志里的文本内容
     */
    public static void logResult(String sWord) {
        FileWriter writer = null;
        try {
            writer = new FileWriter(AlipayConfig.log_path + "alipay_log_" + System.currentTimeMillis()+".txt");
            writer.write(sWord);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /** 
     * 生成文件摘要
     * @param strFilePath 文件路径
     * @param file_digest_type 摘要算法
     * @return 文件摘要结果
     */
    public static String getAbstract(String strFilePath, String file_digest_type) throws IOException {
        PartSource file = new FilePartSource(new File(strFilePath));
        if(file_digest_type.equals("MD5")){
            return DigestUtils.md5Hex(file.createInputStream());
        }
        else if(file_digest_type.equals("SHA")) {
            return DigestUtils.sha256Hex(file.createInputStream());
        }
        else {
            return "";
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122

AlipayNotify.java是验证签名的类

package com.alipay.util;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;

import com.alipay.config.AlipayConfig;
import com.alipay.sign.MD5;

/* *
 *类名:AlipayNotify
 *功能:支付宝通知处理类
 *详细:处理支付宝各接口通知返回
 *版本:3.3
 *日期:2012-08-17
 *说明:
 *以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
 *该代码仅供学习和研究支付宝接口使用,只是提供一个参考

 *************************注意*************************
 *调试通知返回时,可查看或改写log日志的写入TXT里的数据,来检查通知返回是否正常
 */
public class AlipayNotify {

    /**
     * 支付宝消息验证地址
     */
    private static final String HTTPS_VERIFY_URL = "https://mapi.alipay.com/gateway.do?service=notify_verify&";

    /**
     * 验证消息是否是支付宝发出的合法消息
     * @param params 通知返回来的参数数组
     * @return 验证结果
     */
    public static boolean verify(Map<String, String> params) {

        //判断responsetTxt是否为true,isSign是否为true
        //responsetTxt的结果不是true,与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
        //isSign不是true,与安全校验码、请求时的参数格式(如:带自定义参数等)、编码格式有关
        String responseTxt = "false";
        if(params.get("notify_id") != null) {
            String notify_id = params.get("notify_id");
            responseTxt = verifyResponse(notify_id);
        }
        String sign = "";
        if(params.get("sign") != null) {sign = params.get("sign");}
        boolean isSign = getSignVeryfy(params, sign);

        //写日志记录(若要调试,请取消下面两行注释)
        //String sWord = "responseTxt=" + responseTxt + "\n isSign=" + isSign + "\n 返回回来的参数:" + AlipayCore.createLinkString(params);
        //AlipayCore.logResult(sWord);

        if (isSign && responseTxt.equals("true")) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 根据反馈回来的信息,生成签名结果
     * @param Params 通知返回来的参数数组
     * @param sign 比对的签名结果
     * @return 生成的签名结果
     */
    private static boolean getSignVeryfy(Map<String, String> Params, String sign) {
        //过滤空值、sign与sign_type参数
        Map<String, String> sParaNew = AlipayCore.paraFilter(Params);
        //获取待签名字符串
        String preSignStr = AlipayCore.createLinkString(sParaNew);
        //获得签名验证结果
        boolean isSign = false;
        if(AlipayConfig.sign_type.equals("MD5") ) {
            isSign = MD5.verify(preSignStr, sign, AlipayConfig.key, AlipayConfig.input_charset);
        }
        return isSign;
    }

    /**
    * 获取远程服务器ATN结果,验证返回URL
    * @param notify_id 通知校验ID
    * @return 服务器ATN结果
    * 验证结果集:
    * invalid命令参数不对 出现这个错误,请检测返回处理中partner和key是否为空 
    * true 返回正确信息
    * false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
    */
    private static String verifyResponse(String notify_id) {
        //获取远程服务器ATN结果,验证是否是支付宝服务器发来的请求

        String partner = AlipayConfig.partner;
        String veryfy_url = HTTPS_VERIFY_URL + "partner=" + partner + "&notify_id=" + notify_id;

        return checkUrl(veryfy_url);
    }

    /**
    * 获取远程服务器ATN结果
    * @param urlvalue 指定URL路径地址
    * @return 服务器ATN结果
    * 验证结果集:
    * invalid命令参数不对 出现这个错误,请检测返回处理中partner和key是否为空 
    * true 返回正确信息
    * false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
    */
    private static String checkUrl(String urlvalue) {
        String inputLine = "";

        try {
            URL url = new URL(urlvalue);
            HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
            BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection
                .getInputStream()));
            inputLine = in.readLine().toString();
        } catch (Exception e) {
            e.printStackTrace();
            inputLine = "";
        }

        return inputLine;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124

AlipaySubmit.java模拟form表单请求支付宝支付接口的类

package com.alipay.util;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import com.alipay.config.AlipayConfig;
import com.alipay.sign.MD5;

/* *
 *类名:AlipaySubmit
 *功能:支付宝各接口请求提交类
 *详细:构造支付宝各接口表单HTML文本,获取远程HTTP数据
 *版本:3.3
 *日期:2012-08-13
 *说明:
 *以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
 *该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
 */

public class AlipaySubmit {

    /**
     * 支付宝提供给商户的服务接入网关URL(新)
     */
    private static final String ALIPAY_GATEWAY_NEW = "https://mapi.alipay.com/gateway.do?";

    /**
     * 生成签名结果
     * @param sPara 要签名的数组
     * @return 签名结果字符串
     */
    public static String buildRequestMysign(Map<String, String> sPara) {
        String prestr = AlipayCore.createLinkString(sPara); //把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
        String mysign = "";
        if(AlipayConfig.sign_type.equals("MD5") ) {
            mysign = MD5.sign(prestr, AlipayConfig.key, AlipayConfig.input_charset);
        }
        return mysign;
    }

    /**
     * 生成要请求给支付宝的参数数组
     * @param sParaTemp 请求前的参数数组
     * @return 要请求的参数数组
     */
    private static Map<String, String> buildRequestPara(Map<String, String> sParaTemp) {
        //除去数组中的空值和签名参数
        Map<String, String> sPara = AlipayCore.paraFilter(sParaTemp);
        //生成签名结果
        String mysign = buildRequestMysign(sPara);

        //签名结果与签名方式加入请求提交参数组中
        sPara.put("sign", mysign);
        sPara.put("sign_type", AlipayConfig.sign_type);

        return sPara;
    }

    /**
     * 建立请求,以表单HTML形式构造(默认)
     * @param sParaTemp 请求参数数组
     * @param strMethod 提交方式。两个值可选:post、get
     * @param strButtonName 确认按钮显示文字
     * @return 提交表单HTML文本
     */
    public static String buildRequest(Map<String, String> sParaTemp, String strMethod, String strButtonName) {
        //待请求参数数组
        Map<String, String> sPara = buildRequestPara(sParaTemp);
        List<String> keys = new ArrayList<String>(sPara.keySet());

        StringBuffer sbHtml = new StringBuffer();

        sbHtml.append("<form id=\"alipaysubmit\" name=\"alipaysubmit\" action=\"" + ALIPAY_GATEWAY_NEW
                      + "_input_charset=" + AlipayConfig.input_charset + "\" method=\"" + strMethod
                      + "\">");

        for (int i = 0; i < keys.size(); i++) {
            String name = (String) keys.get(i);
            String value = (String) sPara.get(name);

            sbHtml.append("<input type=\"hidden\" name=\"" + name + "\" value=\"" + value + "\"/>");
        }

        //submit按钮控件请不要含有name属性
        sbHtml.append("<input type=\"submit\" value=\"" + strButtonName + "\" style=\"display:none;\"></form>");
        sbHtml.append("<script>document.forms['alipaysubmit'].submit();</script>");

        return sbHtml.toString();
    }



    /**
     * 用于防钓鱼,调用接口query_timestamp来获取时间戳的处理函数
     * 注意:远程解析XML出错,与服务器是否支持SSL等配置有关
     * @return 时间戳字符串
     * @throws IOException
     * @throws DocumentException
     * @throws MalformedURLException
     */
    public static String query_timestamp() throws MalformedURLException,
                                                        DocumentException, IOException {

        //构造访问query_timestamp接口的URL串
        String strUrl = ALIPAY_GATEWAY_NEW + "service=query_timestamp&partner=" + AlipayConfig.partner + "&_input_charset" +AlipayConfig.input_charset;
        StringBuffer result = new StringBuffer();

        SAXReader reader = new SAXReader();
        Document doc = reader.read(new URL(strUrl).openStream());

        List<Node> nodeList = doc.selectNodes("//alipay/*");

        for (Node node : nodeList) {
            // 截取部分不需要解析的信息
            if (node.getName().equals("is_success") && node.getText().equals("T")) {
                // 判断是否有成功标示
                List<Node> nodeList1 = doc.selectNodes("//response/timestamp/*");
                for (Node node1 : nodeList1) {
                    result.append(node1.getText());
                }
            }
        }

        return result.toString();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135

这几个类调用支付宝接口的是AlipaySubmit,在网页选好购买的商品时,在系统中生成订单,然后进行支付,浏览器跳转到支付宝支付网站,Controller中代码为:

/**
     * 支付宝支付页面
     *
     * @return
     * @throws IOException 
     */
    @RequestMapping(value = "/aliPay")
    public void aliPay(HttpServletRequest request, HttpServletResponse response) throws IOException {
        LOGGER.info("支付宝支付页面");
        //商户订单号,商户网站订单系统中唯一订单号,必填
        String orderNo = request.getParameter("orderNo");
        //订单名称,必填
        String subjectName = request.getParameter("subjectName");
        //付款金额,必填
        String total_fee = request.getParameter("fee");
        //商品描述,可空
        String body = "法海风控 " + subjectName;
        if ("money".equals(body)) {
            body = "法海风控  余额充值";
        }
        //把请求参数打包成map
        Map<String, String> sParaTemp = new HashMap<String, String>();
        sParaTemp.put("service", AlipayConfig.service);
        sParaTemp.put("partner", AlipayConfig.partner);
        sParaTemp.put("seller_id", AlipayConfig.seller_id);
        sParaTemp.put("_input_charset", AlipayConfig.input_charset);
        sParaTemp.put("payment_type", AlipayConfig.payment_type);
        sParaTemp.put("notify_url", AlipayConfig.notify_url);
        sParaTemp.put("return_url", AlipayConfig.return_url);
        sParaTemp.put("anti_phishing_key", AlipayConfig.anti_phishing_key);
        sParaTemp.put("exter_invoke_ip", AlipayConfig.exter_invoke_ip);
        sParaTemp.put("out_trade_no", orderNo);
        sParaTemp.put("subject", subjectName);
        sParaTemp.put("total_fee", total_fee);
        sParaTemp.put("body", body);
        //其他业务参数根据在线开发文档,添加参数.文档地址:https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.O9yorI&treeId=62&articleId=103740&docType=1
        //如sParaTemp.put("参数名","参数值");
        //建立请求
        String sHtmlText = AlipaySubmit.buildRequest(sParaTemp,"get","确认");
        response.setHeader("Content-Type", "text/html; charset=UTF-8");
        response.setCharacterEncoding("UTF-8");  
        PrintWriter out = response.getWriter();
        System.out.println(sHtmlText);
        out.println(sHtmlText);

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

请求aliPay会跳转到支付宝支付页面: 
支付宝支付页面


3、接收支付结果通知

在配置好notify_url之后,支付结果会请求相应的接口,我的是order/pay/aliPayOrder,代码如下

/**
     * 支付宝支付订单
     * @return
     * @throws IOException 
     */
    @ResponseBody
    @RequestMapping(value = "pay/aliPayOrder", method = RequestMethod.POST)
    public void aliPayOrder(HttpServletRequest request,HttpServletResponse response) throws IOException {
        LOGGER.info("支付订单");

        //从request中获得参数Map,并返回可读的Map 
        Map<String, String> params = RequestUtil.getParameterMap(request);
        LOGGER.info(params.toString());
        //验证支付宝签名
        boolean aliSign = AlipayNotify.verify(params);
        if (aliSign) {//验证成功
            //交易状态
            String tradeStatus = params.get("trade_status");
            //订单编号          
            String orderNo = params.get("out_trade_no");
            //支付单号
            String payNo = params.get("trade_no");
            //支付账号
            String payAccount = params.get("buyer_email");
            //支付金额
            String totalFee = params.get("total_fee");
            //收款支付宝账号
            String sellerId = params.get("seller_id");
            if (Constant.ALIPAY_TRADE_SUCCESS.equals(tradeStatus)) {//支付宝支付状态为成功
                //验证支付宝返回信息与请求信息一致
                if (ProInfoUtil.getInstance().getProperty("alipay_partner").equals(sellerId)) {
                    //订单处理状态
                    String orderHandleStatus = "error";
                    //验证订单未做支付处理
                    Order order = orderService.queryOrderByOrderNo(orderNo);
                    //订单已支付
                    if (Constant.DEALSTATUS_PAY.equals(order.getDealStatus())) {
                        response.getWriter().print("success");
                        return;
                    }
                    if (null != order && Double.parseDouble(totalFee) == order.getDealPrice() && 
                            Constant.DEALSTATUS_NOT_PAY.equals(order.getDealStatus())) {//验证金额是否和订单一致
                        //更新订单为已支付、更新用户套餐余额、添加用户充值记录、添加用户余额支出记录
                        order.setDealStatus(Constant.DEALSTATUS_PAY);
                        order.setPayNo(payNo);
                        order.setPayType(Constant.ALIPAY);
                        order.setPayAccount(payAccount);
                        try {
                            //支付成功处理支付业务
                            boolean result = orderService.payOrder(order);
                            if (result) {
                                //成功后向支付宝返回成功标志
                                LOGGER.info("支付宝支付成功");
                                orderHandleStatus = "success";
                                response.getWriter().print("success");
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                            LOGGER.info("支付宝支付失败");
                            response.getWriter().print("fail");
                        }

                    }
                    //添加支付信息
                    Map<String, Object> map = new HashMap<String, Object>();
                    map.put("params", params.toString());
                    map.put("payType", Constant.ALIPAY);
                    map.put("orderNo", orderNo);
                    map.put("handleStatus", orderHandleStatus);
                    orderService.addPayInfo(map);
                }
            }

        } else {//验证失败
            LOGGER.info("支付宝返回验证失败");
            response.getWriter().print("fail");
        }
    }


 /** 
  * 从request中获得参数Map,并返回可读的Map 
  *  
  * @param request 
  * @return 
  */  
 @SuppressWarnings("unchecked")  
 public static Map getParameterMap(HttpServletRequest request) {  
     // 参数Map  
     Map properties = request.getParameterMap();  
     // 返回值Map  
     Map<String, String> returnMap = new HashMap<String, String>();  
     Iterator entries = properties.entrySet().iterator();  
     Map.Entry entry;  
     String name = "";  
     String value = "";  
     while (entries.hasNext()) {  
         entry = (Map.Entry) entries.next();  
         name = (String) entry.getKey();  
         Object valueObj = entry.getValue();  
         if(null == valueObj){  
             value = "";  
         }else if(valueObj instanceof String[]){  
             String[] values = (String[])valueObj;  
             for(int i=0;i<values.length;i++){  
                 value = values[i] + ",";  
             }  
             value = value.substring(0, value.length()-1);  
         }else{  
             value = valueObj.toString();  
         }  
         returnMap.put(name, value);  
     }  
     return returnMap;  
 }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115

至此,支付宝支付功能已经做完了,其中有几个细节需要添加,比如支付时查询订单状态是否已经支付,是否过期等等,可以根据自己的需求去完善。 
支付宝的接入还是很顺利的,如果熟练的话一两天就可以完成了,刚开始写博客,有错误或者不明白的地方欢迎大家指出一起交流学习,共同进步。 
由于篇幅问题,我在下一章介绍微信支付的接入。




++++++++++++++++++++++第二个版本++++++++++++++++++++++++++++++++++

引言

支付宝官方电脑网站支付快速接入文档:https://docs.open.alipay.com/270/105899/; 
接下来,我会对这份快速接入文档进行详细的说明。


1、接入支付宝接口的准备

首先,进行一个说明,我做电脑网站接入支付宝接口时,用的是沙箱环境先进行调试的,并且是以java版本为例子的。你需要先注册一个企业号,并且登录支付宝: 
https://open.alipay.com/platform/home.htm

登录进去后,在左上角有一个“开发者中心”按钮,点进去后,在左边有一个“沙箱环境”按钮,点击进入沙箱环境,进行沙箱环境下的支付宝接口开发(在沙箱环境下调试好支付宝的接口后,只需要修改AlipayConfig.java中的配置信息就可以使用了): 
这里写图片描述

1.1 配置沙箱环境

1)首先,需要生成RSA2(SHA256)的应用公钥,并且配置好RSA2(SHA256)的应用公钥,这里注意,不需要配置RSA(SHA1)密钥。 
(生成RSA2的应用公钥详情请参考:https://docs.open.alipay.com/291/105971) 
这里写图片描述

2)!!★★★注意,在沙箱环境下,支付宝的网关与真实的网关有所不同!这里需要注意!

更多的配置沙箱环境的问题可以查阅支付宝官方沙箱环境使用说明文档: 
https://docs.open.alipay.com/200/105311

1.2 下载SDK&Demo

1)我下载的电脑网站支付demo是java版本的,还有PHP版本和.NET版本的,下载地址为: 
https://docs.open.alipay.com/270/106291/

2)下载完demo后,打开里面的readme.txt,按照里面的使用方法一步步完成即可。这里要说明的只有一点,那就是在demo里面还需要下载SDK,将SDK压缩包里面的一些jar包放到工程项目中,就可以了

这里写图片描述

2、接入支付宝

前面已经配置好沙箱环境以及下载好了demo和SDK,接下来要做的工作就是配置AlipayConfig.java文件,并且运行index.jsp文件,查看效果,然后根据自己网站的需求修改index.jsp文件。

2.1 配置AlipayConfig.java

打开AlipayConfig.java文件,可以看到需要配置app_id,RSA2私钥,支付宝公钥,服务器异步通知页面路径,页面跳转同步通知页面路径,支付宝网关。其中,签名方式,字符编码格式是不需要配置的。这个它本身就写好了的。

1)配置app_id,这个app_id就是在沙箱应用中的APPID

2)配置RSA2私钥和支付宝公钥,RSA2私钥是在前面生成好的私钥,而支付宝公钥在上传了应用公钥后,直接点击查看支付宝公钥即可

3)配置服务器异步通知页面路径,★★★注意:这个异步通知页面路径一定要公网可以访问的,不是内网访问!(异步通知的理解可以先转2.2查看接口调用流程),我在这里个人建议可以使用 花生壳软件,绑定本机的ip来进行测试。

4)配置页面跳转同步通知页面路径,这个不需要公网,只需要本机能访问就可以了(同步通知的理解也转2.2查看接口调用流程)

5)支付宝网关,★★★这个一定要注意,使用沙箱环境的支付宝网关和不使用沙箱环境的支付宝网关是不一样的!

注释:有些地方需要配置PID,这个PID就是商户UID

2.2 接口调用流程

这里写图片描述

这里对异步回调和同步回调,进行一个详细的说明。

简单来说,异步回调的意思是不给用户跳转页面,而是执行异步回调页面(即.jsp中的java代码),这里要重点注意的是,因为并不会跳转到该页面,所以并不会执行里面的jsp或者js代码,只会执行java代码。但是,在这里我用的struts2+spring框架,在该jsp页面中调用action的话,在action调用service层时会发生错误。

所以,我在配置异步回调页面的时候,直接通过url执行到action代码,并且通过:

PrintWriter out = ServletActionContext.getResponse().getWriter();
HttpServletRequest request = ServletActionContext.getRequest(); 
  • 1
  • 2

这里我介绍一下,因为支付宝在异步回调的时候,一定要用“out.println”打印出success,支付宝的服务器才会确定这个交易完成了。所以,首先需要定义out,在定义out前,需要得到response,那么接下来,我们介绍一下response:

1、了解response响应

服务器在接收和解释客户端的请求消息后,服务器会返回给客户端一个HTTP响应消息,我们称之为响应(response)。其实也是一个按照http协议的规则拼接而成的一个字符串

在该action中进行商户订单号,交易状态,付款金额,卖家id,商户本身id的验证,防止黑客通过修改该付款金额一类的参数,从而导致网站的损失。并且,这样在action调用service层以及service层调用dao层时都不会发生错误!

同步回调就比较好理解了,就是支付宝在支付完成后,过几秒中会跳转到,你在AlipayConfig中配置的同步回调页面,这个页面可以是自己写的,不需要一定要公网!

3、使用沙箱账号测试支付宝接口

在沙箱环境那一栏,有沙箱账号,使用沙箱账号的买家账号和登录密码进行测试付款






+++++++++++++++++++++++++++++++第三部分++++++++++++

java实现在线支付-支付宝

国内电子商务系统实现的基本流程如下: 
客户在系统内下订单 -> 系统根据订单生成支付宝接口url -> 客户通过url使用支付宝(网上银行)付款 -> 支付宝将客户的付款完成信息发送给电子商务系统 -> 系统收到支付宝信息后确定客户订单已经付款 -> 进行发货等后续流程。

在开始下面的内容之前,你要先有一个支付宝账户,如果要集成支付宝接口,你还必须申请开通服务(关于如何开通,可以直接到支付宝网站上申请).在服务开通后,支付宝会给你2个字符串编号:1个partnerId(合作伙伴ID),还有1个securityCode(安全码).当你拿到这2个码的时候就可以开始下面的内容了.
(1)如何调用支付宝接口?(将客户的订单信息按照既定的规则生成一个url跳转到支付宝网站)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/**   
      * 根据订单生成支付宝接口URL.   
      * @param httpRequest   
      * @param order 订单实例   
      * @return   
      * @throws Exception   
      */    
     public  static  String makeOrderAlipayUrl(HttpServletRequest httpRequest,Order order) throws Exception {     
         HashMap hm =  new  HashMap();     
         hm.put( "_input_charset" ,httpRequest.getCharacterEncoding()); //采用相同的编码方式     
         hm.put( "body" , "您在www.xxx.com上的订单" ); //填写在跳到支付宝页面上显示的付款内容信息     
         hm.put( "discount" , "-5" ); //填写折扣信息 -5表示抵扣5元     
         hm.put( "logistics_fee" , "10" ); //物流费用     
         hm.put( "logistics_payment" , "BUYER_PAY" ); //物流费用支付人 BUYER_PAY=买家支付物流费用     
         hm.put( "logistics_type" , "EXPRESS" ); //物流方式     
         hm.put( "notify_url" , "http://www.xxx.com/notifyurl.jsp" );//客户付款后,支付宝调用的页面     
         hm.put( "out_trade_no" ,order.getId()); //外部交易号,最好具有唯一性,在获取支付宝发来的付款信息时使用.     
         hm.put( "partner" ,partnerId); //partnerId(合作伙伴ID)     
         hm.put( "agent" ,partnerId); //partnerId(合作伙伴ID)     
         hm.put( "payment_type" , "1" ); //支付类型 1=商品购买,2=服务购买,...     
         hm.put( "price" , "105.30" ); //订单金额信息     
         hm.put( "quantity" , "1" ); //订单商品数量,一般都是写1,它是按照整个订单包来计算     
         hm.put( "return_url" , "http://www.xxx.com/ReturnUrl.jsp" );//客户付款成功后,显示给客户的页面     
         hm.put( "seller_email" , "[email protected]" ); //你的支付宝账户email     
         hm.put( "service" , "create_direct_pay_by_user" ); //create_direct_pay_by_user=直接付款,trade_create_by_buyer 担保付款      
         hm.put( "subject" , "www.xxx.com的订单" ); //填写在跳到支付宝页面上显示的付款标题信息     
         String payGateway =  "https://www.alipay.com/cooperate/gateway.do?" ;//跳转到支付宝的url头     
         return  makeUrl(hm,securityCode,httpRequest.getCharacterEncoding(),payGateway); //securityCode(安全码)      
     }     
     /**   
      * 根据传入的参数生成alipay的支付URL   
      * @param hm 参数值   
      * @param securityCode 安全码   
      * @param charset 编码   
      * @param payGateway 支付宝gateway   
      * @return   
      */    
     public  static  String makeUrl(HashMap hm,String securityCode,String charset,String payGateway) throws Exception{     
         List keys =  new  ArrayList(hm.keySet());     
         Collections.sort(keys); //支付宝要求参数必须按字母排序     
         StringBuffer content =  new  StringBuffer();     
         for  ( int  i = 0; i < keys.size(); i++) {     
             content.append((String) keys. get (i));     
             content.append( "=" );     
             content.append((String) hm. get ((String) keys. get (i)));     
             if  (i != keys.size() - 1) {     
                 content.append( "&" );     
             }     
         }     
         content.append(securityCode);     
         String sign = md5(content.toString(),charset);     
         content.delete(0,content.length());     
         content.append(payGateway);     
         for  ( int  i = 0; i < keys.size(); i++) {     
             content.append(keys. get (i));     
             content.append( "=" );     
             content.append(URLEncoder.encode((String) hm. get (keys. get (i)), charset));     
             content.append( "&" );     
         }     
         content.append( "&sign_type=MD5" );     
         keys.clear();     
         keys =  null ;     
         return  content.toString();     
     }     
     /**   
      * 生成md5编码字符串.   
      * @param str 源字符串   
      * @param charset 编码方式   
      * @return   
      *   
      */    
     public  static  String md5(String str,String charset) {     
         if  (str ==  null )     
             return  null ;     
         char  hexDigits[] = {  '0' '1' '2' '3' '4' '5' '6' '7' '8' '9' ,     
                 'a' 'b' 'c' 'd' 'e' 'f'  };     
         MessageDigest md5MessageDigest =  null ;     
         byte [] md5Bytes =  null ;     
         char  md5Chars[] =  null ;     
         byte [] strBytes =  null ;     
         try  {     
             strBytes = str.getBytes(charset);     
             md5MessageDigest = MessageDigest.getInstance( "MD5" );     
             md5MessageDigest.update(strBytes);     
             md5Bytes = md5MessageDigest.digest();     
             int  j = md5Bytes.length;     
             md5Chars =  new  char [j * 2];     
             int  k = 0;     
             for  ( int  i = 0; i < j; i++) {     
                 byte  md5Byte = md5Bytes;     
                 md5Chars[k++] = hexDigits[md5Byte >>> 4 & 0xf];     
                 md5Chars[k++] = hexDigits[md5Byte & 0xf];     
             }     
             return  new  String(md5Chars);     
         catch  (NoSuchAlgorithmException e) {     
             //Log.output(e.toString(), Log.STD_ERR);     
             return  null ;     
         catch  (UnsupportedEncodingException e) {     
             //Log.output(e.toString(), Log.STD_ERR);     
             return  null ;     
         finally  {     
             md5MessageDigest =  null ;     
             strBytes =  null ;     
             md5Bytes =  null ;     
         }     
    

  

当客户通过接口url付款后,支付宝会自动的去调用前面提供的[notify_url]参数中的url.

(2)支付宝将付款信息返回给系统 
当客户付款后,支付宝就会自动调用上面表单提供的[notify_url],下面是一个[notifyurl.jsp]的一个例子:

1
2
3
4
5
6
7
8
<%@ page contentType= "text/html;charset=UTF-8" %><%@ page import= "com.soft4j.AlipayMgr" %><%     
     String ret = AlipayMgr.insert(request);     
     if (ret== null ){     
         out .print( "success" ); //成功接收支付宝发来的付款信息     
     } else {     
         out .print( "fail" ); //出错     
     }     
%>

  

如果确认收到支付宝发来的客户付款信息,则返回"success",这样子支付宝就知道系统已经收到信息了;否则返回"fail",这样支付宝会过一段时间后再次发来。其实,只有当支付宝收到"success"的返回信息后才会停止发送付款信息,否则会自动的每隔一段时间就调用上面 
的[notify_url]通信接口。

(3)系统处理支付宝发来的付款信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/*   
  * Created on 2005-6-12   
  * Author stephen   
  * Email zhoujianqiang AT gmail DOT com   
  * CopyRight(C)2005-2008 , All rights reserved.   
  */    
package com.soft4j;     
import java.sql.Connection;     
import java.sql.SQLException;     
import java.util.Enumeration;     
import java.util.Vector;     
import javax.servlet.http.HttpServletRequest;     
/**   
  * 支付宝付款通知接口.   
  *    
  * @author stephen   
  * @version 1.0.0   
  */    
public  final  class  NotifyUrlMgr {     
     public  static  String insert(HttpServletRequest httpRequest) {     
         //定义变量和进行必要的初始化工作     
         Enumeration parameterNames =  null ;     
         String parameterName =  null ;     
         String parameterValue =  null ;     
         int  count = 0;     
         Vector[]  params  null ;     
         Vector vParameterName =  new  Vector();     
         Vector vParameterValue =  new  Vector();     
         try  {     
             String orderId = httpRequest.getParameter( "out_trade_no" ); //订单号     
             if (orderId== null || "" . equals (orderId)) orderId= "-1" ;     
             parameterNames = httpRequest.getParameterNames();     
             boolean isPrint =  false ;     
             while  (parameterNames.hasMoreElements()) { //循环收取支付宝发来的所有参数信息     
                 parameterName = (String) parameterNames.nextElement();     
                 parameterValue = httpRequest.getParameter(parameterName);     
                 if (parameterValue== null ) parameterValue= "" ;     
                 vParameterName.add(parameterName);     
                 vParameterValue.add(parameterValue);     
                 count++;     
             }     
             //这里添加对收到信息的处理:一般是将这些信息存入数据库,然后对客户的订单进行处理.     
             return  null ;     
         catch  (Exception e) {     
             return  e.toString();     
         finally  {      
         }     
     }     
}




























++++++++++++++++++++++++++++++第五部分++++++++++++++++++++++++++++

1、在线支付概述

什么是在线支付呢?没错,就是在网上花钱!大家一定有过这样的经历。但是你可能不太了解在线支付的“内情”,下面我们来了解一下!

如果你现在开始经营一个电子商务网站,用户买了东西一定要支付,你的网站一定要可以连接各大银行了,然后在各大银行支付完成后,再返回到你的网站上显示“支付成功”!

这就是今天我们要做的事情,连接银行的网银系统完成支付。说专业一点,我们称之为“开发在线支付的网关”

2、两种在线支付的方式

在线支付一共有两种方式:

*电商直接与银行对接
*电商通过第三方支付平台与银行对接

电商直接与银行对接,这也要银行同意才行,但可惜的是,银行很“牛”,不是谁想与它对接都可以的。如果你的电商每日的资金流量够大,那么银行会和你对接,因为客户支付给电商的钱都存到了银行的帐户中!但是如果资金流量小,银行不会理你的!

当小网站资金量不足时,不能与银行对接,那么它们会选择与第三方支付公司合作。大家也都明白这是些什么公司,例如:支付宝、易宝、财富通、快钱等公司是国内比较有名的。它们这些公司可以与银行对接(因为资金够多),然后小电商与它们对接!但是第三方是要求收费的!第三方一般会收取电商1%的费用,不过不会收客户的钱。

通过上图大家可以了解到,在银行的页面上会显示出商城名称、RMB订单号、订单时间。。。,这些东西银行是怎么知道的,当然是电商传递给银行的。当电商与银行对接后,电商要给银行的页面传递银行页面需要的参数,所以银行的页面才能显示这些数据!

但是,我们的商城不能只可以对接一家银行吧!怎么也要对接BOC、CCB、ABC、ICBC四家吧!不同的银行需要的对接参数是不相同的,这说明我们在开发时要为不同的银行写不同的对接代码。这也是直接与银行对接的缺点!当然与银行直接对接也有好处,就是安全,没有手续费!

为不同的银行开发不同的代码(缺点);
安全(优点);
没有手续费(优点);
小电商银行不让对接(缺点)。

上图中已经说明,客户在电商的网站上点击确认支付后,会定向到第三方的网站,然后再由第三方与银行对接。这说明电商要传递给第三方参数!再由第三方把参数传递给银行。这种方式的好处是:只需要针对第三方开发即可,而不用再为每家银行提供参数。为每家银行提供参数的工作是第三方的任务了。但是,第三方不老可靠的,如果第三方倒闭了,人跑了,那你的钱就没了。因为客户支付的钱没有到你的银行帐户中,而是支付到了第三方的银行帐户中,而你是在第三方有一个帐户。而且第三方还要收手续费,一般是1%,这可不是小数字啊(真黑)。

3、通过第三方在线支付规则

电商想在第三方注册商户,需要向第三方提供ICP认证。ICP经营许可证是根据国家《互联网管理办法规定》,经营性网站必须办理的网站经营许可证,没有就属于非法经营。

我们不可能因为练习就去办理ICP!所以我们无法在第三方注册商户。不过我们已经有现成的在易宝注册的商户,所以这一步就可以忽略了。

当你在易宝注册成功后,易宝会给你如下几样东西:

在易宝的开户账号(即商户编码):10001126856
易宝接入规范:一个chm文件
对称加密算法类:PaymentUtil.java
密钥:69cl522AV6q613Ii4W6u8K6XuW8vM1N6bFgyv769220IuYe9u37N4y7rI4Pl
在易宝接入规范中,我们可以查找到易宝的支付网关,其实就是一个URL,用来与易宝对接的一个网址:https://www.yeepay.com/app-merchant-proxy/node

在易宝接入规范中,还可以查找到易宝要求的参数,在电商与易宝对接时需要给支付网关传递这些参数:

正式请求地址:https://www.yeepay.com/app-merchant-proxy/node

这些参数需要追加到URL后面。

但是要注意,这些参数的值需要加密。加密的密钥和加密算法易宝都会提供!

其中p8_Url表示当支付成功后,返回到电商的哪个页面。这说明我们需要写一个显示结果的页面。第三方在支付成功后,会重定向到我们指定的返回页面,而且还会带给我们一些参数,我们的页面需要获取这些参数,显示在页面中。下面是第三方返回的参数:

4、开发第三方在线支付系统

步骤:

index.jsp页面:一个表单,提交到BuyServlet,表单项有:订单编号、付款金额、选择银行
BuyServlet:获取表单数据,准备连接第三方网关。因为在index.jsp页面中只给出3个参数,而第三方需要的参数有N多,页面没有给出的参数由BuyServlet补充。而且参数还需要加密,这也需要在BuyServlet中完成
BackServlet:当用户支付成功后,第三方会重定向到我们指定的返回页面,我们使用BackServlet作为返回页面,它用来接收第三方传递的参数,显示在页面中

因为已经有了在易宝的注册商号,所以我们就不用自己去注册商号了。所以这里使用易宝做为第三方支付平台来测试。因为我本人没有电商(必须通过ICP认证的电商),所以也不能在第三方注册商号。

我们现在使用的易宝商号是由传智播客提供的,巴巴运动网在易宝注册的商号。所以在测试时支付的钱都给了巴巴运动网在易宝注册的商号了。

第一步:index.jsp

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
< form action = "" method = "post" >
订单号:< input type = "text" name = "p2_Order" />< br />
金 额:< input type = "text" name = "p3_Amt" />< br />
选择银行:< br />
< input type = "radio" name = "pd_FrpId" value = "ICBC-NET-B2C" />工商银行
< img src = "bank_img/icbc.bmp" align = "middle" />
< input type = "radio" name = "pd_FrpId" value = "BOC-NET-B2C" />中国银行
< img src = "bank_img/bc.bmp" align = "middle" />< br />< br />
< input type = "radio" name = "pd_FrpId" value = "ABC-NET-B2C" />农业银行
< img src = "bank_img/abc.bmp" align = "middle" />
< input type = "radio" name = "pd_FrpId" value = "CCB-NET-B2C" />建设银行
< img src = "bank_img/ccb.bmp" align = "middle" />< br />< br />
< input type = "radio" name = "pd_FrpId" value = "BOCO-NET-B2C" />交通银行
< img src = "bank_img/bcc.bmp" align = "middle" />< br />
< input type = "submit" value = "确认支付" />
</ form >

每个银行对应的值:

第二步:BuyServlet.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class BuyServlet extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding( "utf-8" );
response.setContentType( "text/html;charset=utf-8" );
String p0_Cmd = "Buy" ; // 业务类型,固定值为buy,即“买”
String p1_MerId = "10001126856" ; // 在易宝注册的商号
String p2_Order = request.getParameter( "p2_Order" ); // 订单编号
String p3_Amt = request.getParameter( "p3_Amt" ); // 支付的金额
String p4_Cur = "CNY" ; // 交易种币,固定值为CNY,表示人民币
String p5_Pid = "" ; // 商品名称
String p6_Pcat = "" ; // 商品各类
String p7_Pdesc = "" ; // 商品描述
String p8_Url = "http://localhost:8080/buy/BackServlet" ;// 电商的返回页面,当支付成功后,易宝会重定向到这个页面
String p9_SAF = "" ; // 送货地址
String pa_MP = "" ; // 商品扩展信息
String pd_FrpId = request.getParameter( "pd_FrpId" ); // 支付通道,即选择银行
String pr_NeedResponse = "1" ; // 应答机制,固定值为1
// 密钥,由易宝提供,只有商户和易宝知道这个密钥。
String keyValue = "69cl522AV6q613Ii4W6u8K6XuW8vM1N6bFgyv769220IuYe9u37N4y7rI4Pl" ;
// 通过上面的参数、密钥、加密算法,生成hmac值
// 参数的顺序是必须的,如果没有值也不能给出null,而应该给出空字符串。
String hmac = PaymentUtil.buildHmac(p0_Cmd, p1_MerId, p2_Order, p3_Amt,
p4_Cur, p5_Pid, p6_Pcat, p7_Pdesc, p8_Url, p9_SAF, pa_MP,
pd_FrpId, pr_NeedResponse, keyValue);
// 把所有参数连接到网关地址后面
url += "?p0_Cmd=" + p0_Cmd +
"&p1_MerId=" + p1_MerId +
"&p2_Order=" + p2_Order +
"&p3_Amt=" + p3_Amt +
"&p4_Cur=" + p4_Cur +
"&p5_Pid=" + p5_Pid +
"&p6_Pcat=" + p6_Pcat +
"&p7_Pdesc=" + p7_Pdesc +
"&p8_Url=" + p8_Url +
"&p9_SAF=" + p9_SAF +
"&pa_MP=" + pa_MP +
"&pd_FrpId=" + pd_FrpId +
"&pr_NeedResponse=" + pr_NeedResponse +
"&hmac=" + hmac;
System.out.println(url);
// 重定向到网关
response.sendRedirect(url);
}
}

第三步:BackServlet

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class BackServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType( "text/html;charset=utf-8" );
/*
* 易宝会提供一系列的结果参数,我们获取其中需要的即可
* 获取支付结果:r1_Code,1表示支付成功。
* 获取支付金额:r3_Amt
* 获取电商的订单号:r6_Order
* 获取结果返回类型:r9_BType,1表示重定向返回,2表示点对点返回,
* 但点对点我们收不到,因为我们的ip都是局域网ip。
*/
String r1_Code = request.getParameter( "r1_Code" );
String r3_Amt = request.getParameter( "r3_Amt" );
String r6_Order = request.getParameter( "r6_Order" );
String r9_BType = request.getParameter( "r9_BType" );
if (r1_Code.equals( "1" )) {
if (r9_BType.equals( "1" )) {
response.getWriter().print( "<h1>支付成功!</h1>" ); //其实支付不成功时根本易宝根本就不会返回到本Servlet
response.getWriter().print( "支付金额为:" + r3_Amt + "<br/>" );
response.getWriter().print( "订单号为:" + r6_Order + "<br/>" );
}
}
}
}

易宝支付提供的获取hmac的工具类

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
public class PaymentUtil {
private static String encodingCharset = "UTF-8" ;
/**
* 生成hmac方法
*
* @param p0_Cmd 业务类型
* @param p1_MerId 商户编号
* @param p2_Order 商户订单号
* @param p3_Amt 支付金额
* @param p4_Cur 交易币种
* @param p5_Pid 商品名称
* @param p6_Pcat 商品种类
* @param p7_Pdesc 商品描述
* @param p8_Url 商户接收支付成功数据的地址
* @param p9_SAF 送货地址
* @param pa_MP 商户扩展信息
* @param pd_FrpId 银行编码
* @param pr_NeedResponse 应答机制
* @param keyValue 商户密钥
* @return
*/
public static String buildHmac(String p0_Cmd,String p1_MerId,
String p2_Order, String p3_Amt, String p4_Cur,String p5_Pid, String p6_Pcat,
String p7_Pdesc,String p8_Url, String p9_SAF,String pa_MP,String pd_FrpId,
String pr_NeedResponse,String keyValue) {
StringBuilder sValue = new StringBuilder();
// 业务类型
sValue.append(p0_Cmd);
// 商户编号
sValue.append(p1_MerId);
// 商户订单号
sValue.append(p2_Order);
// 支付金额
sValue.append(p3_Amt);
// 交易币种
sValue.append(p4_Cur);
// 商品名称
sValue.append(p5_Pid);
// 商品种类
sValue.append(p6_Pcat);
// 商品描述
sValue.append(p7_Pdesc);
// 商户接收支付成功数据的地址
sValue.append(p8_Url);
// 送货地址
sValue.append(p9_SAF);
// 商户扩展信息
sValue.append(pa_MP);
// 银行编码
sValue.append(pd_FrpId);
// 应答机制
sValue.append(pr_NeedResponse);
return PaymentUtil.hmacSign(sValue.toString(), keyValue);
}
/**
* 返回校验hmac方法
*
* @param hmac 支付网关发来的加密验证码
* @param p1_MerId 商户编号
* @param r0_Cmd 业务类型
* @param r1_Code 支付结果
* @param r2_TrxId 易宝支付交易流水号
* @param r3_Amt 支付金额
* @param r4_Cur 交易币种
* @param r5_Pid 商品名称
* @param r6_Order 商户订单号
* @param r7_Uid 易宝支付会员ID
* @param r8_MP 商户扩展信息
* @param r9_BType 交易结果返回类型
* @param keyValue 密钥
* @return
*/
public static boolean verifyCallback(String hmac, String p1_MerId,
String r0_Cmd, String r1_Code, String r2_TrxId, String r3_Amt,
String r4_Cur, String r5_Pid, String r6_Order, String r7_Uid,
String r8_MP, String r9_BType, String keyValue) {
StringBuilder sValue = new StringBuilder();
// 商户编号
sValue.append(p1_MerId);
// 业务类型
sValue.append(r0_Cmd);
// 支付结果
sValue.append(r1_Code);
// 易宝支付交易流水号
sValue.append(r2_TrxId);
// 支付金额
sValue.append(r3_Amt);
// 交易币种
sValue.append(r4_Cur);
// 商品名称
sValue.append(r5_Pid);
// 商户订单号
sValue.append(r6_Order);
// 易宝支付会员ID
sValue.append(r7_Uid);
// 商户扩展信息
sValue.append(r8_MP);
// 交易结果返回类型
sValue.append(r9_BType);
String sNewString = PaymentUtil.hmacSign(sValue.toString(), keyValue);
return sNewString.equals(hmac);
}
/**
* @param aValue
* @param aKey
* @return
*/
public static String hmacSign(String aValue, String aKey) {
byte k_ipad[] = new byte [ 64 ];
byte k_opad[] = new byte [ 64 ];
byte keyb[];
byte value[];
try {
keyb = aKey.getBytes(encodingCharset);
value = aValue.getBytes(encodingCharset);
} catch (UnsupportedEncodingException e) {
keyb = aKey.getBytes();
value = aValue.getBytes();
}
Arrays.fill(k_ipad, keyb.length, 64 , ( byte ) 54 );
Arrays.fill(k_opad, keyb.length, 64 , ( byte ) 92 );
for ( int i = 0 ; i < keyb.length; i++) {
k_ipad[i] = ( byte ) (keyb[i] ^ 0x36 );
k_opad[i] = ( byte ) (keyb[i] ^ 0x5c );
}
MessageDigest md = null ;
try {
md = MessageDigest.getInstance( "MD5" );
} catch (NoSuchAlgorithmException e) {
return null ;
}
md.update(k_ipad);
md.update(value);
byte dg[] = md.digest();
md.reset();
md.update(k_opad);
md.update(dg, 0 , 16 );
dg = md.digest();
return toHex(dg);
}
public static String toHex( byte input[]) {
if (input == null )
return null ;
StringBuffer output = new StringBuffer(input.length * 2 );
for ( int i = 0 ; i < input.length; i++) {
int current = input[i] & 0xff ;
if (current < 16 )
output.append( "0" );
output.append(Integer.toString(current, 16 ));
}
return output.toString();
}
/**
*
* @param args
* @param key
* @return
*/
public static String getHmac(String[] args, String key) {
if (args == null || args.length == 0 ) {
return ( null );
}
StringBuffer str = new StringBuffer();
for ( int i = 0 ; i < args.length; i++) {
str.append(args[i]);
}
return (hmacSign(str.toString(), key));
}
/**
* @param aValue
* @return
*/
public static String digest(String aValue) {
aValue = aValue.trim();
byte value[];
try {
value = aValue.getBytes(encodingCharset);
} catch (UnsupportedEncodingException e) {
value = aValue.getBytes();
}
MessageDigest md = null ;
try {
md = MessageDigest.getInstance( "SHA" );
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null ;
}
return toHex(md.digest(value));
}
// public static void main(String[] args) {
// System.out.println(hmacSign("AnnulCard1000043252120080620160450.0http://localhost/SZXpro/callback.asp杩?4564868265473632445648682654736324511","8UPp0KE8sq73zVP370vko7C39403rtK1YwX40Td6irH216036H27Eb12792t"));
// }
}

易宝回调

点对点:易宝直接访问电商,这里没有客户端什么事了

这种方式是必须要使用的,我们这种方式是收不到的!因为我们没有固定IP
易宝有一个重发机制,如果它访问你,你不给它回信息,它会一直重发!
电商需要返回一个以SUCCESS开头的字符串即可!

引导客户端浏览器重定向到电商。是让客户端访问电商!

可以不使用的!

hmac:13参数值+keyValue(密钥) + 算法(md5)

13参数值:自己设置的!
keyValue:易宝在我们注册后发给我们的,这个东东只有我们和易宝知道!
底层为md5的算法:PaymentUtil.buildHmac(14个),它返回hmac


猜你喜欢

转载自blog.csdn.net/zhh1072773034/article/details/79804624