GVKun编程网logo

支付接口——WeChat / Alipay(微信支付宝官方支付接口)

1

在这篇文章中,我们将带领您了解支付接口——WeChat/Alipay的全貌,包括微信支付宝官方支付接口的相关情况。同时,我们还将为您介绍有关AliPay、AlipayCrossborderWebsit

在这篇文章中,我们将带领您了解支付接口——WeChat / Alipay的全貌,包括微信支付宝官方支付接口的相关情况。同时,我们还将为您介绍有关AliPay、Alipay Cross border Website Payment for Opencart 2、Alipay Cross-border Magento Module V2.5 发布、alipay nodejs check ATN的知识,以帮助您更好地理解这个主题。

本文目录一览:

支付接口——WeChat / Alipay(微信支付宝官方支付接口)

支付接口——WeChat / Alipay(微信支付宝官方支付接口)

1.微信支付

  a.微信支付官方文档地址:https://pay.weixin.qq.com/wiki/doc/api/index.html

  b.在官网下载SDK

  c.使用IDEA打开 java_sdk_v3.0.9,修改 WXPayConfig 类,将访问修饰符都改为 public

package com.github.wxpay.sdk;

import java.io.InputStream;

public abstract class WXPayConfig {



    /**
     * 获取 App ID
     *
     * @return App ID
     */
    public abstract String getAppID();


    /**
     * 获取 Mch ID
     *
     * @return Mch ID
     */
    public abstract String getMchID();


    /**
     * 获取 API 密钥
     *
     * @return API密钥
     */
    public abstract String getKey();


    /**
     * 获取商户证书内容
     *
     * @return 商户证书内容
     */
    public abstract InputStream getCertStream();

    /**
     * HTTP(S) 连接超时时间,单位毫秒
     *
     * @return
     */
    public int getHttpConnectTimeoutMs() {
        return 6*1000;
    }

    /**
     * HTTP(S) 读数据超时时间,单位毫秒
     *
     * @return
     */
    public int getHttpReadTimeoutMs() {
        return 8*1000;
    }

    /**
     * 获取WXPayDomain, 用于多域名容灾自动切换
     * @return
     */
    public abstract IWXPayDomain getWXPayDomain();

    /**
     * 是否自动上报。
     * 若要关闭自动上报,子类中实现该函数返回 false 即可。
     *
     * @return
     */
    public boolean shouldAutoReport() {
        return true;
    }

    /**
     * 进行健康上报的线程的数量
     *
     * @return
     */
    public int getReportWorkerNum() {
        return 6;
    }


    /**
     * 健康上报缓存消息的最大数量。会有线程去独立上报
     * 粗略计算:加入一条消息200B,10000消息占用空间 2000 KB,约为2MB,可以接受
     *
     * @return
     */
    public int getReportQueueMaxSize() {
        return 10000;
    }

    /**
     * 批量上报,一次最多上报多个数据
     *
     * @return
     */
    public int getReportBatchSize() {
        return 10;
    }

}

 

 

  d.使用 maven 将项目 install 到本地仓库

 

  e.pom文件中引入导入的SDK依赖

<dependency>
      <groupId>com.github.wxpay</groupId>
      <artifactId>wxpay-sdk</artifactId>
      <version>3.0.9</version>
    </dependency>

 

 

  f.新建 MyConfig 类继承 WXPayConfig

public class MyConfig extends WXPayConfig{
    //公众账号ID
    public static final String APP_ID = "";
    //商户号
    public static final String MCH_ID = "";
    //API密钥
    public static final String KEY = "";
    //异步支付回调地址(外网)
    public static final String NOTIFY_URL = "";
    //异步退款回调地址(外网)
    public static final String REFUND_NOTIFY_URL = "";

    //---------------------------------------

    private byte[] certData;

    public MyConfig() throws Exception {
        String certPath = "/path/to/apiclient_cert.p12";
        File file = new File(certPath);
        InputStream certStream = new FileInputStream(file);
        this.certData = new byte[(int) file.length()];
        certStream.read(this.certData);
        certStream.close();
    }

    public String getAppID() {
        return APP_ID;
    }

    public String getMchID() {
        return MCH_ID;
    }

    public String getKey() {
        return KEY;
    }

    public InputStream getCertStream() {
        ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
        return certBis;
    }

    public IWXPayDomain getWXPayDomain() {
        // 这个方法需要这样实现, 否则无法正常初始化WXPay
        IWXPayDomain iwxPayDomain = new IWXPayDomain() {
            public void report(String domain, long elapsedTimeMillis, Exception ex) {
            }

            public DomainInfo getDomain(WXPayConfig config) {
                return new IWXPayDomain.DomainInfo(WXPayConstants.DOMAIN_API, true);
            }
        };
        return iwxPayDomain;
    }

    public int getHttpConnectTimeoutMs() {
        return 8000;
    }

    public int getHttpReadTimeoutMs() {
        return 10000;
    }

}

 

 

  g.统一下单、查询订单、退款

public class WechatUtil {

    /**
     * 统一下单(预支付)
     * @param outTradeNo 外部订单号
     * @param title 标题
     * @param totalFee 总金额
     * @param spbillCreateIp 客户端IP
     * @return
     * @throws Exception
     */
    public static Map<String, String> prepay(String outTradeNo, String title, int totalFee, String spbillCreateIp) throws Exception {
        MyConfig config = new MyConfig();
        WXPay wxpay = new WXPay(config);

        Map<String, String> data = new HashMap<String, String>();
        data.put("body", title);
        data.put("out_trade_no", outTradeNo);
        data.put("fee_type", "CNY");
        data.put("total_fee", String.valueOf(totalFee));
        data.put("spbill_create_ip", spbillCreateIp);
        data.put("notify_url", MyConfig.NOTIFY_URL);
        data.put("trade_type", "NATIVE");  // 此处指定为扫码支付
        data.put("product_id", "12"); //扫码支付时必传参数

        Map<String, String> result = new HashMap<>();
        try {
            Map<String, String> resp = wxpay.unifiedOrder(data);
            System.out.println(resp);
            if("SUCCESS".equals(resp.get("return_code"))){
                if("SUCCESS".equals(resp.get("result_code"))){
                    String prepay_id = resp.get("prepay_id");
                    String code_url = resp.get("code_url");

                    HashMap<String, String> signMap = new HashMap<String, String>();
                    signMap.put("appid", MyConfig.APP_ID);
                    signMap.put("partnerid", MyConfig.MCH_ID);
                    signMap.put("prepayid", prepay_id);
                    signMap.put("package", "Sign=WXPay");
                    signMap.put("noncestr", WXPayUtil.generateNonceStr());
                    signMap.put("timestamp", String.valueOf(System.currentTimeMillis()/1000));
                    String sign = WXPayUtil.generateSignature(signMap, MyConfig.KEY);

                    result.put("prepay_id", prepay_id);
                    result.put("code_url", code_url);
                    result.put("sign", sign);
                    result.putAll(signMap);
                    return result;

                }else{
                    System.out.println(resp.get("err_code_des"));
                }
            }else{
                System.out.println(resp.get("return_msg"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 查询订单
     * @param outTradeNo 外部订单号
     * @throws Exception
     */
    public static void tradeQuery(String outTradeNo) throws Exception {
        MyConfig config = new MyConfig();
        WXPay wxpay = new WXPay(config);

        Map<String, String> data = new HashMap<String, String>();
        data.put("out_trade_no", outTradeNo);

        try {
            Map<String, String> resp = wxpay.orderQuery(data);
            System.out.println(resp);
            if("SUCCESS".equals(resp.get("return_code"))){
                if("SUCCESS".equals(resp.get("result_code"))){
                    //TODO 根据response中的结果继续业务逻辑处理

                }else{
                    System.out.println(resp.get("err_code_des"));
                }
            }else{
                System.out.println(resp.get("return_msg"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     *  退款
     * @param outTradeNo 外部订单号
     * @param outRefundNo 外部退款订单号
     * @param totalFee 总金额
     * @param refundFee 退款金额
     * @throws Exception
     */
    public static void refund(String outTradeNo, String outRefundNo, int totalFee, int refundFee) throws Exception {
        MyConfig config = new MyConfig();
        WXPay wxpay = new WXPay(config);

        Map<String, String> data = new HashMap<String, String>();
        data.put("out_trade_no", outTradeNo);
        data.put("out_trade_no", outTradeNo);
        data.put("out_refund_no", outRefundNo);
        data.put("total_fee", String.valueOf(totalFee));
        data.put("refund_fee", String.valueOf(refundFee));
        data.put("notify_url", MyConfig.REFUND_NOTIFY_URL);

        try {
            Map<String, String> resp = wxpay.refund(data);
            System.out.println(resp);
            if("SUCCESS".equals(resp.get("return_code"))){
                if("SUCCESS".equals(resp.get("result_code"))){
                    //TODO 根据response中的结果继续业务逻辑处理

                }else{
                    System.out.println(resp.get("err_code_des"));
                }
            }else{
                System.out.println(resp.get("return_msg"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

 

 

  h.支付/退款 回调

@RestController
public class WechatController {

    @RequestMapping("/payment/wechatResult")
    public String wechatResult(HttpServletRequest request){
        try{
            InputStream is = request.getInputStream();
            //将InputStream转换成String
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            StringBuilder sb = new StringBuilder();
            String line = null;
            try {
                while ((line = reader.readLine()) != null) {
                    sb.append(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            String resXml=sb.toString();
            System.out.println(resXml);

            //-------------------------------------------------------------------

            MyConfig config = new MyConfig();
            WXPay wxpay = new WXPay(config);
            Map<String, String> notifyMap = WXPayUtil.xmlToMap(resXml);         // 转换成map
            if (wxpay.isPayResultNotifySignatureValid(notifyMap)) {
                // 签名正确
                // 进行处理。
                // 注意特殊情况:订单已经退款,但收到了支付结果成功的通知,不应把商户侧订单状态从退款改成支付成功
                String return_code = notifyMap.get("return_code");//状态
                String out_trade_no = notifyMap.get("out_trade_no");//订单号

                if (out_trade_no == null) {
                    return "";
                }
                //TODO 根据notifyMap中的结果继续业务逻辑处理


                return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml> ";
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return "";
    }

    @RequestMapping("/payment/wechatRefundResult")
    public String wechatRefundResult(HttpServletRequest request){
        try {
            BufferedReader reader = request.getReader();
            StringBuilder sb = new StringBuilder();
            String line = null;
            try {
                while ((line = reader.readLine()) != null) {
                    sb.append(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            String resXml=sb.toString();
            System.out.println(resXml);

            //---------------------------------------------------

            Map<String, String> notifyMap = WXPayUtil.xmlToMap(resXml);
            if ("SUCCESS".equals(notifyMap.get("return_code"))) {
                String req_info = notifyMap.get("req_info");

                /**
                 * 解密方式
                 * 解密步骤如下:
                 * (1)对加密串A做base64解码,得到加密串B
                 * (2)对商户key做md5,得到32位小写key* ( key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置 )
                 * (3)用key*对加密串B做AES-256-ECB解密(PKCS7Padding)
                 */
                byte[] byteArray = (new BASE64Decoder()).decodeBuffer(req_info);
                String key = DigestUtils.md5Hex(MyConfig.KEY.getBytes("UTF-8"));

                SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES");
                Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
                cipher.init(Cipher.DECRYPT_MODE, secretKey);
                String resultStr = new String(cipher.doFinal(byteArray));

                Map<String, String> aesMap = WXPayUtil.xmlToMap(resultStr);
                //TODO 根据aesMap中的结果继续业务逻辑处理


                return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml> ";
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return "";
    }
}

 

 

 

2.支付宝支付

  a.官方文档地址:https://docs.open.alipay.com/204/105297/

  b.在pom文件中引入SDK依赖

<dependency>
      <groupId>com.alipay.sdk</groupId>
      <artifactId>alipay-sdk-java</artifactId>
      <version>3.7.89.ALL</version>
    </dependency>

 

 

  c.统一下单、交易查询、交易退款

public class AlipayUtil {
    //HTTPS请求地址
    public static final String URL = "https://openapi.alipay.com/gateway.do";
    //异步回调地址(外网)
    public static final String NOTIFY_URL = "";
    //应用ID
    public static final String APP_ID = "";
    //应用私钥
    public static final String APP_PRIVATE_KEY = "";
    //支付宝公钥
    public static final String ALIPAY_PUBLIC_KEY = "";
    //编码格式
    public static final String CHARSET = "utf-8";


    /**
     * 生成APP支付订单信息
     * @param outTradeNo 外部订单号
     * @param title 标题
     * @param totalAmount 总金额
     * @return
     */
    public static String getOrderStr(String outTradeNo,String title,String totalAmount){
        //实例化客户端
        AlipayClient alipayClient = new DefaultAlipayClient(URL, APP_ID, APP_PRIVATE_KEY, "json", CHARSET, ALIPAY_PUBLIC_KEY, "RSA2");
        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        //SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setSubject(title);
        model.setOutTradeNo(outTradeNo);
        model.setTotalAmount(totalAmount);
        model.setProductCode("QUICK_MSECURITY_PAY");
        request.setBizModel(model);
        request.setNotifyUrl(NOTIFY_URL);
        try {
            //这里和普通的接口调用不同,使用的是sdkExecute
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
            if(response.isSuccess()){
                String orderStr = response.getBody();//就是orderString 可以直接给客户端请求,无需再做处理。
                System.out.println(orderStr);
                return orderStr;
            }else{
                System.out.println(response.getSubCode());
            }

        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 交易查询
     * @param outTradeNo 外部订单号
     */
    public static void tradeQuery(String outTradeNo){
        AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", APP_ID, APP_PRIVATE_KEY, "json", CHARSET, ALIPAY_PUBLIC_KEY, "RSA2"); //获得初始化的AlipayClient
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();//创建API对应的request类
        request.setBizContent("{" +
                "   \"out_trade_no\":\"" + outTradeNo + "\"," +
                "  }");//设置业务参数
        try {
            AlipayTradeQueryResponse response = alipayClient.execute(request);//通过alipayClient调用API,获得对应的response类
            if(response.isSuccess()){
                System.out.print(response.getBody());
                //TODO 根据response中的结果继续业务逻辑处理

            }else{
                System.out.println(response.getSubCode());
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
    }

    /**
     * 交易退款
     * @param outTradeNo 外部订单号
     * @param refundAmount 退款金额
     */
    public static void tradeRefund(String outTradeNo, String refundAmount){
        AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", APP_ID, APP_PRIVATE_KEY, "json", CHARSET, ALIPAY_PUBLIC_KEY, "RSA2"); //获得初始化的AlipayClient
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();//创建API对应的request类
        request.setBizContent("{" +
                "    \"out_trade_no\":\"" + outTradeNo + "\"," +
                "    \"refund_amount\":\"" + refundAmount + "\"" +
                "  }");//设置业务参数
        try {
            AlipayTradeRefundResponse response = alipayClient.execute(request);//通过alipayClient调用API,获得对应的response类
            if(response.isSuccess()){
                System.out.print(response.getBody());
                //TODO 根据response中的结果继续业务逻辑处理

            }else{
                System.out.println(response.getSubCode());
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
    }

}

 

 

  d.支付回调

@RestController
public class AlipayController {

    @RequestMapping("/payment/alipayResult")
    public String alipayResult(HttpServletRequest request){
        //获取支付宝POST过来反馈信息
        Map<String,String> params = new HashMap<String,String>();
        Map requestParams = request.getParameterMap();
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            //乱码解决,这段代码在出现乱码时使用。
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }
        //切记alipaypublickey是支付宝的公钥,请去open.alipay.com对应应用下查看。
        //boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)
        try {
            boolean flag = AlipaySignature.rsaCheckV1(params, AlipayUtil.ALIPAY_PUBLIC_KEY, AlipayUtil.CHARSET,"RSA2");
            if(flag){
                //TODO 根据params中的结果继续业务逻辑处理

                return "success";
            }else{
                return "failure";
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return "failure";
    }
    
}

 

AliPay

AliPay

AliPay

  • 阿里的支付也就是支付宝,官网已经出了Python的SDK了,所以先下载:pip3 install alipay-sdk-python 网址
  • 支付宝沙箱环境的地址:https://openhome.alipay.com/platform/appDaily.htm?tab=info

一、使用详情:

第1步:访问沙箱环境地址,注册一个商家账户点击

    • 密钥生成方式1(推荐):生成RSA密钥
    • 密钥生成方式2:OpenSSL工具生成密钥
    • 按照上面方式生成商户的公钥和私钥,把公钥放入网站私钥自己留着签名用,设置应用公钥后

    • 下面是商户信息以及卖家信息下载一个支付宝的沙箱环境

  • 然后按照下面的账号和密码登录和支付,只支持安卓

第2步:阅读文档 找到一个适合自己的API 点击

  • 这里选择:统一收单下单并支付页面接口 点击
  • 仔细阅读文档按照文档的参数要求去向接口发请求

第3步:代码实例

  • 回调url的测试要放在公网IP上,所以测试的时候放入自己服务器.
  • alipay-sdk-python
from django.conf.urls import url
from django.contrib import admin
from app01.views import AliPayView, PayHandlerView

urlpatterns = [
    url(r''^admin/'', admin.site.urls),
    url(r''^pay$'', AliPayView.as_view()),
    url(r''^alipay_handler'', PayHandlerView.as_view()),
]
urls.py
from django.shortcuts import render, redirect
from django.http import HttpResponse
from rest_framework.views import APIView
from rest_framework.response import Response
from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient
from alipay.aop.api.AlipayClientConfig import AlipayClientConfig
from alipay.aop.api.domain.AlipayTradePrecreateModel import AlipayTradePrecreateModel
from alipay.aop.api.request. AlipayTradePrecreateRequest import AlipayTradePrecreateRequest
from alipay.aop.api.domain.AlipayTradePagePayModel import AlipayTradePagePayModel
from alipay.aop.api.request.AlipayTradePagePayRequest import AlipayTradePagePayRequest
import time

# Create your views here.
# 沙箱环境地址:https://openhome.alipay.com/platform/appDaily.htm?tab=info

# "https://openapi.alipaydev.com/gateway.do", APP_ID, APP_PRIVATE_KEY, "json", CHARSET, ALIPAY_PUBLIC_KEY, "RSA2"


def ali_pay():
    # 为阿里支付实例化一个配置信息对象
    alipay_config = AlipayClientConfig(sandbox_debug=True)
    # 初始化各种配置信息
    # 阿里提供服务的接口
    alipay_config.server_url = "https://openapi.alipaydev.com/gateway.do"
    # 申请的沙箱环境的app_id
    alipay_config.app_id = "2016091800540924"
    # 商户的私钥
    with open("keys/app_private_key.txt") as f:
        alipay_config.app_private_key = f.read()
    # 阿里的公钥
    with open("keys/alipay_public_key.txt") as f:
        alipay_config.alipay_public_key = f.read()
    # 实例化一个支付对象并返回
    alipay_client = DefaultAlipayClient(alipay_client_config=alipay_config)
    return alipay_client


class AliPayView(APIView):
    def get(self, request):
        return render(request, "pay.html")

    # 生成支付宝自带页面的API
    def post(self, request):
        # 得到阿里支付的实例化对象
        client = ali_pay()
        # 为API生成一个模板对象 初始化参数用的
        model = AlipayTradePagePayModel()
        # 订单号
        model.out_trade_no = "pay" + str(time.time())
        # 金额
        model.total_amount = 8888
        # 商品标题
        model.subject = "测试"
        # 商品详细内容
        model.body = "支付宝测试"
        # 销售产品码,与支付宝签约的产品码名称
        model.product_code = "FAST_INSTANT_TRADE_PAY"
        # 实例化一个请求对象
        request = AlipayTradePagePayRequest(biz_model=model)
        # get请求 用户支付成功后返回的页面请求地址
        request.return_url = "http://140.143.63.45:8000/alipay_handler"
        # post请求 用户支付成功通知商户的请求地址
        request.notify_url = "http://140.143.63.45:8000/alipay_handler"
        # 利用阿里支付对象发一个获得页面的请求 参数是request
        response = client.page_execute(request, http_method="GET")
        return redirect(response)


class PayHandlerView(APIView):

    def get(self, request):
        # return_url的回调地址
        print(request.data)
        # 用户支付成功之后回到哪
        return HttpResponse("return_url测试")

    def post(self, request):
        print(request.data)
        # 用户支付成功 在这里修改订单状态以及优惠券贝里等等情况
        return HttpResponse("notify_url")
app01中views.py
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
{#    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>#}


</head>
<body>
<form method="POST">
        {% csrf_token %}
    <input type="text" name="money">
    <input type="submit" value="去支付" />
</form>

</body>
</html>
templates中pay.html

二、旧版

from datetime import datetime
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from urllib.parse import quote_plus
from urllib.parse import urlparse, parse_qs
from base64 import decodebytes, encodebytes
import json


class AliPay(object):
    """
    支付宝支付接口(PC端支付接口)
    """

    def __init__(self, appid, app_notify_url, app_private_key_path,
                 alipay_public_key_path, return_url, debug=False):
        self.appid = appid
        self.app_notify_url = app_notify_url
        self.app_private_key_path = app_private_key_path
        self.app_private_key = None
        self.return_url = return_url
        with open(self.app_private_key_path) as fp:
            self.app_private_key = RSA.importKey(fp.read())
        self.alipay_public_key_path = alipay_public_key_path
        with open(self.alipay_public_key_path) as fp:
            self.alipay_public_key = RSA.importKey(fp.read())

        if debug is True:
            self.__gateway = "https://openapi.alipaydev.com/gateway.do"
        else:
            self.__gateway = "https://openapi.alipay.com/gateway.do"

    def direct_pay(self, subject, out_trade_no, total_amount, return_url=None, **kwargs):
        biz_content = {
            "subject": subject,
            "out_trade_no": out_trade_no,
            "total_amount": total_amount,
            "product_code": "FAST_INSTANT_TRADE_PAY",
            # "qr_pay_mode":4
        }

        biz_content.update(kwargs)
        data = self.build_body("alipay.trade.page.pay", biz_content, self.return_url)
        return self.sign_data(data)

    def build_body(self, method, biz_content, return_url=None):
        data = {
            "app_id": self.appid,
            "method": method,
            "charset": "utf-8",
            "sign_type": "RSA2",
            "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "version": "1.0",
            "biz_content": biz_content
        }

        if return_url is not None:
            data["notify_url"] = self.app_notify_url
            data["return_url"] = self.return_url

        return data

    def sign_data(self, data):
        data.pop("sign", None)
        # 排序后的字符串
        unsigned_items = self.ordered_data(data)
        unsigned_string = "&".join("{0}={1}".format(k, v) for k, v in unsigned_items)
        sign = self.sign(unsigned_string.encode("utf-8"))
        # ordered_items = self.ordered_data(data)
        quoted_string = "&".join("{0}={1}".format(k, quote_plus(v)) for k, v in unsigned_items)

        # 获得最终的订单信息字符串
        signed_string = quoted_string + "&sign=" + quote_plus(sign)
        return signed_string

    def ordered_data(self, data):
        complex_keys = []
        for key, value in data.items():
            if isinstance(value, dict):
                complex_keys.append(key)

        # 将字典类型的数据dump出来
        for key in complex_keys:
            data[key] = json.dumps(data[key], separators=('','', '':''))

        return sorted([(k, v) for k, v in data.items()])

    def sign(self, unsigned_string):
        # 开始计算签名
        key = self.app_private_key
        signer = PKCS1_v1_5.new(key)
        signature = signer.sign(SHA256.new(unsigned_string))
        # base64 编码,转换为unicode表示并移除回车
        sign = encodebytes(signature).decode("utf8").replace("\n", "")
        return sign

    def _verify(self, raw_content, signature):
        # 开始计算签名
        key = self.alipay_public_key
        signer = PKCS1_v1_5.new(key)
        digest = SHA256.new()
        digest.update(raw_content.encode("utf8"))
        if signer.verify(digest, decodebytes(signature.encode("utf8"))):
            return True
        return False

    def verify(self, data, signature):
        if "sign_type" in data:
            sign_type = data.pop("sign_type")
        # 排序后的字符串
        unsigned_items = self.ordered_data(data)
        message = "&".join(u"{}={}".format(k, v) for k, v in unsigned_items)
        return self._verify(message, signature)
utils/pay.py
from django.shortcuts import render,redirect,HttpResponse
from django.views.decorators.csrf import csrf_exempt
from utils.pay import AliPay
import time
from django.conf import settings


#依赖:pip3 install pycryptodome

def aliPay():
    obj = AliPay(
        appid=settings.APPID,
        app_notify_url=settings.NOTIFY_URL,  # 如果支付成功,支付宝会向这个地址发送POST请求(校验是否支付已经完成)
        return_url=settings.RETURN_URL,  # 如果支付成功,重定向回到你的网站的地址。
        alipay_public_key_path=settings.PUB_KEY_PATH,  # 支付宝公钥
        app_private_key_path=settings.PRI_KEY_PATH,  # 应用私钥
        debug=True,  # 默认False,
    )
    return obj

def index(request):
    if request.method == ''GET'':
        return render(request,''index.html'')



    alipay = aliPay()

    # 对购买的数据进行加密
    money = float(request.POST.get(''price''))
    out_trade_no = "x2" + str(time.time())
    # 1. 在数据库创建一条数据:状态(待支付)

    query_params = alipay.direct_pay(
        subject="刘亦菲",  # 商品简单描述
        out_trade_no= out_trade_no,  # 商户订单号
        total_amount=money,  # 交易金额(单位: 元 保留俩位小数)
    )

    pay_url = "https://openapi.alipaydev.com/gateway.do?{}".format(query_params)

    return redirect(pay_url)


def pay_result(request):
    """
    支付完成后,跳转回的地址
    :param request:
    :return:
    """
    params = request.GET.dict()
    sign = params.pop(''sign'', None)

    alipay = aliPay()

    status = alipay.verify(params, sign)

    if status:
        return HttpResponse(''支付成功'')
    return HttpResponse(''支付失败'')



@csrf_exempt
def update_order(request):
    """
    支付成功后,支付宝向该地址发送的POST请求(用于修改订单状态)
    :param request:
    :return:
    """
    if request.method == ''POST'':
        from urllib.parse import parse_qs

        body_str = request.body.decode(''utf-8'')
        post_data = parse_qs(body_str)

        post_dict = {}
        for k, v in post_data.items():
            post_dict[k] = v[0]

        alipay = aliPay()

        sign = post_dict.pop(''sign'', None)
        status = alipay.verify(post_dict, sign)
        if status:
            # 修改订单状态
            out_trade_no = post_dict.get(''out_trade_no'')
            print(out_trade_no)
            # 2. 根据订单号将数据库中的数据进行更新
            return HttpResponse(''支付成功'')
        else:
            return HttpResponse(''支付失败'')
    return HttpResponse('''')
views.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r''^admin/'', admin.site.urls),
    url(r''^index/'', views.index),
    url(r''^pay_result/'', views.pay_result),
    url(r''^update_order/'', views.update_order),
]
urls.py
ALLOWED_HOSTS = ["*",]
# 支付相关配置
APPID = "2016082500309412"
NOTIFY_URL = "http://1.1.1.1:80/update_order/"
RETURN_URL = "http://1.1.1.1:80/pay_result/"
PRI_KEY_PATH = "keys/app_private_2048.txt"
PUB_KEY_PATH = "keys/alipay_public_2048.txt"
settings.py

 

Alipay Cross border Website Payment for Opencart 2

Alipay Cross border Website Payment for Opencart 2

经过近一周辛苦的编码、调试、实际支付测试, Alipay境外支付插件的Opencart版本发布了!支持Opencart 2.0及以上版本。市场上虽然之前已经有了类似的支付模块,但发现多少都存在以下问题:

1,没有结算货币设置功能(结算货币是商户和支付宝协商的收款币种),虽然不设置结算货币,大多时候也可以支付,但不符合支付宝接口文档规范。
2,没有人民币"直传"功能,人民币直传指当客户订单货币为RMB(CNY)时,无需汇率转换,直接传递人民币金额到支付宝网关。
3,支付宝消息通知功能缺失,如遇网络故障,或者客户支付后没有跳转回商户网站,可能会导致漏单或者订单状态不同步问题。
4,没有支付日志功能,Log功能是记录支付过程中,支付插件和支付宝服务器通讯过程中的消息数据,当支付出现问题时候,可以查看日志信息,来快速查明原因。

由此,决定开发出一系列完善的Opencart支付宝模块。先来看看Alipay Cross-border Webstie Payment Extension for Opencart 2.x支付模块。

Opencart支付方式选择界面,采用最新的蓝色支付宝Logo图片

提交订单后,就跳转来到了支付宝收银台,支付宝采用国际即时汇率,自动将订单金额换算成人民币。Alipay Cross-border Payment属于即时到账交易。支付宝最近优化了收银台页面,重点突出手机钱包扫一扫支付,手机扫码后,输入支付宝支付密码,成功后就跳转回商户网站了。也可以点击右侧【登陆账户付款】,然后输入支付宝账户和密码登陆后支付。

我们的Opencart Cross-border Website Payment支付模块实现了支付宝异步通知功能,如果用户支付后,立即关闭了浏览器,或者突然网络中断、抑或是突然断电关机,没有按照正常逻辑跳转回商户网站,这时候,订单也会生成,同时订单状态也会正确更新为已付款!

Opencart网站支付成功页面。

Opencart网站管理员后台,订单列表,可以看到支付成功订单状态为: Proccessing

Opencart网站管理员后台,Order History界面,订单备注中包含有alipay notify字样,表示该订单是收到支付宝消息通知时建立的。正常情况下,在用户支付后跳转回网站前,支付宝消息通知已经领先一步到达商户网站所在服务器了。我们的支付插件,接收到通知消息后,就赶紧保存订单,并更新支付状态。

Opencart Cross-border Website Payment Extension参数设置界面。Settlement Currency是支付宝结算给商户的货币。Order Status是客户支付成功后的订单状态。Log功能是记录支付过程中,支付插件和支付宝服务器通讯过程中的消息数据,当支付出现问题时候,可以查看日志信息,来快速查明原因。

Alipay Cross-border Magento Module V2.5 发布

Alipay Cross-border Magento Module V2.5 发布

收到客户邮件询问,支付方式为Alipay Cross-border Website Payment, 为什么支付成功后,在Alipay后台订单列表中的订单号后面多出了一些数字,如下图所示:

原因是在V2.5新版本中,增加了订单号冲突解决机制。

如果客户有两个以上Magento站点,使用同一个Alipay支付账户,那么订单号就很有可能重复,在支付的时候,Alipay就会报错。

因此呢,Alipay Cross-border Payment支付模块,在Magento标准的订单号后面,增加了4位随机数字,就近乎100%避免了这个问题的发生。当然了,如果你只有一个Magento站点,或者是各个Magento站点采用不同的订单号前缀,不会发生订单号重复问题,可以在支付模块中禁用该选项,这样提交到Alipay的订单号就不会带奇怪的随机数字了。

Perfect Alipay Extension!

alipay nodejs check ATN

alipay nodejs check ATN

请问有人用nodejs 来 接入 alipay么? 我今天尝试的时候 ,在 notify的时候,check ATN,请求到服务器的时候,一直 

{ [Error: getaddrinfo ENOTFOUND] code: ''ENOTFOUND'', errno: ''ENOTFOUND'', syscall: ''getaddrinfo'' }

               //获取远程服务器ATN结果,验证是否是支付宝服务器发来的请求


                var partner = AlipayConfig.partner;
                var veryfy_path = AlipayConfig.HTTPS_VERIFY_PATH + "partner=" + partner + "&notify_id=" + params["notify_id"];


                requestUrl(AlipayConfig.ALIPAY_HOST,veryfy_path,function(responseTxt){
                    if(responseTxt){
                        callback(true);
                    }else{
                        callback(false);
                    }
                });



今天关于支付接口——WeChat / Alipay微信支付宝官方支付接口的分享就到这里,希望大家有所收获,若想了解更多关于AliPay、Alipay Cross border Website Payment for Opencart 2、Alipay Cross-border Magento Module V2.5 发布、alipay nodejs check ATN等相关知识,可以在本站进行查询。

本文标签: