银联支付开发、使用的一些总结

2023-11-11

 

        现在的网页支付(PC和微信H5)和app支付,用的比较多的是微信支付、银联支付和支付宝支付,其余的是这些支付的第三方支付,我目前了解的只有这么多。我目前做了银联支付和微信支付,这里说一些银联支付的开发的一些介绍吧。

        根据我们公司的应用经验,银联支付时费率最低的,如果和银联商务谈判的好,自身的交易量比较大,费率可能更低(具体不能透露了)。银联支付一种是按百分比收取手续费,另一种按笔数收取手续费,例如,每笔0.9元。我们公司用了2种银联支付,一开始用的是银联支付,后来因费率问题,使用银联支付第三方平台--千引支付,实质上也是银联支付。

      我们申请的银联支付,在网上营业厅、微信服务号、Android和iOS平台都在使用,是“手机WAP支付产品”。主要用了2个接口,第一个是“消费类交易”,即银联支付申请接口。它也可以分为两部分,(1)通过拼接报文,签名加密,生成html网页,浏览器跳转请求到银联支付页面。此时,订单生成,用户可以在银联页面完成支付。(2)支付成功后,银联会根据请求参数里的配置,主动回调前台、后台通知地址,通知用户支付成功。第二个接口是“交易状态查询交易”,即查询订单交易结果。

     现在具体讲一下代码:

    消费类交易接口:官方文档地址:https://open.unionpay.com/ajweb/help/api

(1)交易申请。初始化证书,拼接报文,数据签名,创建表单或取得tn号。调用此接口,微信、网厅可得到一个html的https post表单,浏览器执行js自动提交,访问银联支付申请接口,跳转银联支付页面。此时,服务端只是拼接报文,签名加密,请求的动作,是用户的浏览器做的,可以保证银联支付的安全性。app端则是,调用接口,取得tn号,调用app已安装的银联控件跳往银联支付页面。

private String pc2UnionPay(String orderId, String incMoney,
            String txnTime) {
        /**
         * 初始化证书
         */
        SDKConfig.getConfig().loadPropertiesFromSrc();// 从classpath加载acp_sdk.properties文件

        /**
         * 交易请求url 从配置文件读取
         */
        String requestFrontUrl = SDKConfig.getConfig().getFrontRequestUrl();

        /**
         * 组装请求报文
         */
        Map<String, String> data = new HashMap<String, String>();
        // 版本号
        data.put("version", "5.0.0");
        // 字符集编码 默认"UTF-8"
        String encoding = "UTF-8";
        data.put("encoding", encoding);
        // 签名方法 01 RSA
        data.put("signMethod", "01");
        // 交易类型 01-消费
        data.put("txnType", "01");
        // 交易子类型 01:自助消费 02:订购 03:分期付款
        data.put("txnSubType", "01");
        // 业务类型 000201 B2C网关支付
        data.put("bizType", "000201");
        // 渠道类型 07-互联网渠道
        data.put("channelType", "07");
        // 商户/收单前台接收地址 选送
        // 后台服务对应的写法参照 FrontRcvResponse.java
        data.put("frontUrl", frontUrl);
        // 商户/收单后台接收地址 必送
        // 后台服务对应的写法参照 BackRcvResponse.java
        data.put("backUrl", backUrl);
        // 接入类型:商户接入填0 0- 商户 , 1: 收单, 2:平台商户
        data.put("accessType", "0");
        // 商户号码
        data.put("merId", merId);
        // 订单号 商户根据自己规则定义生成,每订单日期内不重复
        data.put("orderId", orderId);
        // 订单发送时间 格式: YYYYMMDDhhmmss 商户发送交易时间,根据自己系统或平台生成
        data.put("txnTime", txnTime);

        // 交易金额 分
        data.put("txnAmt", incMoney);
        // 交易币种
        data.put("currencyCode", "156");

        // 若报文中的数据元标识的key对应的value为空,不上送该报文域

        /**
         * 创建表单
         */
        String html = null;
        for (int i = 1; i <= 3; i++) {
            html = createHtml(requestFrontUrl, signData(data));
            if (html != null) {
                return html;
            }
        }
        return "服务器忙";
    }

public static String createHtml(String action, Map<String, String> hiddens) {
        StringBuffer sf = new StringBuffer();
        sf.append("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/></head><body>");
        sf.append("<form id = \"pay_form\" action=\"" + action
                + "\" method=\"post\">");
        if (null != hiddens && 0 != hiddens.size()) {
            Set<Entry<String, String>> set = hiddens.entrySet();
            Iterator<Entry<String, String>> it = set.iterator();
            while (it.hasNext()) {
                Entry<String, String> ey = it.next();
                String key = ey.getKey();
                String value = ey.getValue();
                sf.append("<input type=\"hidden\" name=\"" + key + "\" id=\""
                        + key + "\" value=\"" + value + "\"/>");
            }
        }
        sf.append("</form>");
        sf.append("</body>");
        sf.append("<script type=\"text/javascript\">");
        sf.append("document.all.pay_form.submit();");
        sf.append("</script>");
        sf.append("</html>");
        return sf.toString();
    }

(2)支付成功通知商户。根据申请时取得的前后台通知地址,银联在支付成功后,会主动通知商户交易结果,但应以后台通知为准。前台应答是通过 浏览器,用户点击“返回商户”按钮发送给商户的,后台通知是银联系统异步把交易状态为成功的交易通知给商户,失败交易不发送。前台应答及后台通知,商户都需要验签。

public class BackNoticeServlet extends HttpServlet {
    private static final long serialVersionUID = -900467945307979675L;
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        try {
            LogUtil.writeLog("后台接收银行通知开始……");
            BankService bService = (BankService) PayCache.getInstance().getBean("bankService");
            RechargeService service =  (RechargeService)PayCache.getInstance().getBean("rechargeService");
//            request.setCharacterEncoding("ISO-8859-1");
            String encoding = request.getParameter(SDKConstants.param_encoding);
            // 获取请求参数中所有的信息
            Map<String, String> reqParam = getAllRequestParam(request);
            // 打印请求报文
            LogUtil.printRequestLog(reqParam);

            Map<String, String> valideData = null;
            if (null != reqParam && !reqParam.isEmpty()) {
                Iterator<Entry<String, String>> it = reqParam.entrySet()
                        .iterator();
                valideData = new HashMap<String, String>(reqParam.size());
                while (it.hasNext()) {
                    Entry<String, String> e = it.next();
                    String key = (String) e.getKey();
                    String value = (String) e.getValue();
                    value = new String(value.getBytes("ISO-8859-1"), encoding);
                    valideData.put(key, value);
                }
                Map<String, String> res = new HashMap<String, String>();
                // 验证签名
                if (!SDKUtil.validate(valideData, encoding)) {
                    LogUtil.writeLog("验证签名结果[失败].");
                } else {
                    LogUtil.writeLog("验证签名结果[成功].");
                    // 持久化银行返回的数据
                    LogUtil.writeLog("持久化银行返回的数据");
                    String settleDate = reqParam.get("settleDate");
                    Calendar cal=Calendar.getInstance();
                    settleDate = String.valueOf(cal.get(Calendar.YEAR)) + settleDate;//加上年份
                    reqParam.put("settleDate", settleDate);
                    service.addPayresult(reqParam);
                    if("00".equals(reqParam.get("respCode"))){
                        service.updOrder(reqParam.get("orderId"),settleDate);
                    }
                    LogUtil.writeLog("返回银行数据给商户开始……");
                    res.put("respCode", reqParam.get("respCode"));
                    res.put("respMsg", reqParam.get("respMsg"));
                    res.put("orderId", reqParam.get("orderId"));
                    res.put("txnTime", reqParam.get("txnTime"));
                    res.put("txnAmt", reqParam.get("txnAmt"));
                    res.put("queryId", reqParam.get("queryId"));
                    res.put("settleDate", reqParam.get("settleDate"));
                    res.put("settleAmt", reqParam.get("settleAmt"));
                    res.put("txnType", reqParam.get("txnType"));
                    String statusCode = "";
                    ProfitApplyOrder order = bService.queryOrder(reqParam.get("orderId"));
                    for(int i=0;i<5;i++){
                        statusCode = XmlUtil.invokeMethod(res,order.getBackUrl());
                        if(statusCode.equals("success")){
                            LogUtil.writeLog("返回银行数据给商户结束[成功].");
                            break;
                        }
                    }
                    LogUtil.writeLog("银行数据接受完成!");
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
            LogUtil.writeLog("接受银行数据失败:" + e.getMessage());
        } finally {
            response.setHeader("Content-type", "text/html;charset=UTF-8");
            PrintWriter writer = response.getWriter();
            writer.print(System.currentTimeMillis());
            writer.flush();
        }
    }
    
    public static Map<String, String> getAllRequestParam(
            final HttpServletRequest request) {
        Map<String, String> res = new HashMap<String, String>();
        Enumeration<?> temp = request.getParameterNames();
        if (null != temp) {
            while (temp.hasMoreElements()) {
                String en = (String) temp.nextElement();
                String value = request.getParameter(en);
                res.put(en, value);
                // 在报文上送时,如果字段的值为空,则不上送<下面的处理为在获取所有参数数据时,判断若值为空,则删除这个字段>
                // System.out.println(" temp数据的键=="+en+"     值==="+value);
                if (null == res.get(en) || "".equals(res.get(en))) {
                    res.remove(en);
                }
            }
        }
        return res;
    }
}

 

交易状态查询交易:(以下摘自银联官方文档)

商户收到后台通知后,应根据通知报文中订单号等关键信息,更新系统中的订单支付状态。
商户在以下情况下,可主动发起交易状态查询,并根据查询结果更新交易状态。
(1)前台交易,在5分钟内未收到后台通知。
(2)后台交易,在1秒内未收到后台通知 。
(3)商户在特殊情况下没有正确处理后台通知,需要重新判定交易状态。
商户应注意对同一笔交易,对不同时间点可能先后收到的查询结果及后台通知进行合适的处理。

以上,此接口调用查询订单交易结果,是商户主动发起的,没什么好说的,把官方文档贴了一下。附上交易应答码地址:https://open.unionpay.com/ajweb/help?id=262

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

银联支付开发、使用的一些总结 的相关文章

  • 分享常用JDBC连接参数

    oracle 驱动 oracle jdbc driver OracleDriver URL jdbc oracle thin
  • 依靠自我

    必读网 http www beduu com 整理 依靠自我 我们需要爱默生式的思想家 当所有的编译工作都完成之后 我突然发现自己在编译过程中经常出现的 为什么要编译爱默生的文章 的疑问都变得多余了 也就是说 我突然认为 在中国重提爱默生是
  • iOS:如何在iphone、ipad上安装一些常用命令行命令

    iOS 如何在iphone ipad上安装一些常用命令行命令 相信对Linux Unix比较熟悉的朋友 在iphone或 ipad越狱后发现通过Cydia可以安装OpenSSH 一定都想安装上并且通过ssh登录上去看看 但是登录后却发现几乎
  • jsch jar包连接不上ssh报Algorithm negotiation fail 错误

    参考 JSchException Algorithm negotiation fail问题解决之路 GreatQing的个人页面 OSCHINA 中文开源技术交流社区 1 jsch jar包连接不上ssh报Algorithm negotia
  • python 报错error193_WindowsError:[错误193]%1不是在Python有效的Win32应用程序

    I wish to import liblas module in Python 2 7 on window 64bit If I import the module with IDLE Python GUI I have no probl
  • 相较稳定的红包算法实现/c语言实现

    红包的分配分布 个人认为能够运用到正态分布是极好 运气好的人少 运气差的人也少 但是本篇算法并不打算利用正态分布的特性来实现分配红包算法 本人太菜 而是一个产生红包相对稳定 一定程度上也算一个是符合正太分布特性的算法 世间很多事物的分布一定
  • 爬虫的请求参数字典中含有相同的键该怎么办?

    目录 针对GET方法 针对POST方法 针对GET方法 比如一个网站get方法的链接是 https www baidu com assist test get name test age 20 class age 18 可以看出该方法传递的
  • 计算机网络 学习摘要(8) - 音视频服务/IPv6协议

    概述 音视频常用的协议 实时流RTSP协议 实时运输协议RTP 实时传送控制协议RTCP H 323 以及会话发起协议SIP 声音采用的是PCM编码 不同的是数据率影响音质 互联网本身是非等时的 音视频传输需要时间间隔等时 互联网提供的音频
  • BigDecimal精度丢失,给前端返回String类型,ToStringSerializer。

    bigDecimal长度太长 返回给前端 精度会丢失 即后几位都会变成0 解决办法 给前端返回字符串类型 加注解 JsonSerialize using ToStringSerializer class JsonSerialize usin
  • 蓝桥杯历届-带分数

    蓝桥杯历届 带分数 标题 带分数 100 可以表示为带分数的形式 100 3 69258 714 还可以表示为 100 82 3546 197 注意特征 带分数中 数字1 9分别出现且只出现一次 不包含0 类似这样的带分数 100 有 11
  • 使用Chrome调试JavaScript的断点设置和调试技巧

    使用Chrome调试JavaScript的断点设置和调试技巧 投稿 hebedich 字体 增加 减小 类型 转载 时间 2014 12 16 这篇文章主要介绍了使用Chrome调试JavaScript的断点设置和调试技巧 需要的朋友可以参
  • centos7 开机启动流程

    开机启动流程 开机启动流程的意义 1 掌握让某个软件开机自动运行 2 开机不能正常启动 是什么原因 或者哪个环节出现了问题 3 防止黑客植入木马 去查询黑客会把木马放到哪些地方 POST 上电自检 gt 加载BIOS Basic Input
  • 处理点云数据 点云与生成前视图

    创建点云数据中的前视图 前视图投影 为了将激光雷达传感器的前视图平面化为二维图像 必须将三维空间中的点投影到可以展开的圆柱形表面上 以将其平面化 下面公式见论文Vehicle Detection from 3D Lidar Using Fu
  • 快捷指令_快捷指令怎么用?玩转快捷指令(捷径)全攻略 看完你就是老司机!...

    快捷指令大全下载 快捷指令 捷径 下载 https m zmtc com jiejing 下载安装快捷指令 请复制以上网址 在iOS设备自带的 Safari 浏览器中打开进行下载与安装即可 快捷指令果粉朋友一定不会陌生 不过一直以来 我们主
  • Spring框架概述 --- 常用注解

    Spring框架概述 常用注解 装配注解 SpringMVC注解 Spring配置注解 装配注解 Spring 提供了以下多个注解 这些注解可以直接标注在 Java 类上 将它们定义成 Spring Bean Component 该注解用于
  • tomcat端口被占用解决办法

    tomcat启动时出现Address already in use JVM Bind 的原因是因为端口被占用 有可能是因为多次启动tomcat或者启动了多个tomcat 或者是其他应用程序或者服务占用了 错误日志如下 严重 Standard

随机推荐

  • C++学习之用数组实现顺序表

    开始学习数据结构 路还长 加油 用C 的template实现顺序表SequenceList 第一次上传完整代码 比较菜 SequenceList h ifndef SEQ LIST H define SEQ LIST H include
  • 【100%通过率 】【华为OD机试 c++/java/python】最小调整顺序次数【 2023 Q1

    华为OD机试 题目列表 2023Q1 点这里 2023华为OD机试 刷题指南 点这里 题目描述 给定一个队列 但是这个队列比较特殊 可以从头部添加数据 也可以从尾部添加数据 但是只能从头部删除数据 输入一个数字n 会依次添加数字1 n 也就
  • JavaScript中sort方法排序不正确解决办法

    假如你想对一个数组排序 在JS中你会想到sort函数 例如 var arr 1 100 20 200 40 50 120 10 arr sort console log arr 控制台中可以看到结果是 1 10 100 120 20 200
  • 气压计MS5611编程操作流程

    文章更新版本 时间 备注 修订人 V0 1 发布于 2019 09 21 11 03 46 初始版本 libo 文章目录 一 开发笔记 二 初始化与工作流程 1 从PROM读取出厂校准值 2 启动温度AD转换 读取AD值 3 启动气压AD转
  • TaskCompletionSource

    概括 TaskCompletionSource
  • 极速加密文件夹: 一个2秒加密文件夹的Windows文件夹加密软件

    大部分朋友的电脑上 经常会有一些重要文件 隐私照片和视频不想让别人轻易可以访问或者打开 尤其你的电脑可能会被别人偶尔使用的时候 加密保护就尤其重要了 不少Windows用户通常使用ZIP等压缩工具 将文件夹压缩成压缩包并加上密码进行文件保护
  • 图像语义分割综述(semantic segmentation) 上篇

    本文对图像语义分割近年来的主要发展做一个综述性的介绍 翻译了以下两篇博文 并进行了整合 https www jeremyjordan me semantic segmentation http blog qure ai notes sema
  • Linux 最全的添加开机启动方法

    添加开启启动的方式一般有如下几种 第一种 把开机命令 写入到os的启动文件之中 伴随着启动文件的读取 顺便帮你把要启动的任务 一起启动了 第二种 把启动任务 放到os的启动目录下 os启动过程之中 会执行所有放在此目录下的任务 第三种 os
  • 利用Arthas+APM监控进行Java性能深度定位

    大家可能都用过APM监控 包括开源的Skywalking 商用的卓豪 ZOHO ManageEngine APM应用性能监控 以及云监控产品如听云 Server监控 这些APM监控产品大大方便了我们实时监控应用性能 并实现性能深度透视监控
  • JavaScript 日期对象Date(声明/Date对象的方法/返回距离1970/01/01毫秒数)

    Date 1 日期对象声明 var date1 new Date 设定制定时间 兼容最强 var date2 new Date 2016 01 27 12 00 00 不常用 var date3 new Date Wed Jan 27 20
  • .NET当中的另一个V8 JS引擎 Wrapper

    前言 之前写过一篇文章 是关于在 NET中 使用v8sharp作为v8Engine的Wraper 从而在 NET中与Javascript互操作 不过v8Sharp有几个很大的问题 第一 在参数中传递中文字符串会产生乱码 第二 无法在 NET
  • 篡改检查是什么?.NET混淆器Dotfuscator如何实现应用程序的保护?

    Dotfuscator是一个 NET的Obfuscator 它提供企业级的应用程序保护 大大降低了盗版 知识产权盗窃和篡改的风险 Dotfuscator的分层混淆 加密 水印 自动失效 防调试 防篡改 报警和防御技术 为世界各地成千上万的应
  • spring-security-oauth2 登录或者认证成功后 做一些操作, 比如登录日志。

    通过跟踪代码 发现放当我们访问 oauth token 这个请求的方法在TokenEndpoint 获取access token 的时候 身份认证成功后会在 ProviderManager的authenticate 方法 通过eventPu
  • Java学习(100)Java多线程——实现Callable接口创建线程

    Java多线程 实现Callable接口创建线程 步骤 案例 实现Callable接口创建线程 创建多线程还会有其他方式 我们再介绍一种 1 继承Thread类 重写run 方法 run 方法代表线程要执行的任务 2 实现Runnable接
  • Python随笔1(持续更新中)

    一 Python对于缩进有着极其严格的要求 Python的缩进规则等效于C语言中的 符号 Python中的tab或者空格的多少代表语句的等级 或者代表其中谁的子语句 例如以下Python与C语言的for循环语句 Python语言的for循环
  • 【docker】docker-compose安装带ui页面的kafka集群

    docker compose 安装带kafka ui 的kafka集群 在日常的工作当中 kafka集群作为常用的中间件 其搭建过程略显繁琐 需要配置的文件颇多 为了方便各位初学者快速体验kafka的魅力 本文采取一键式安装kafka 3
  • 华为机试(字符串消消乐---Java实现)

    package test import java util Scanner 消消乐 字符串 描述 给定一个只包含大小写字母的字符串 大小写敏感 若相邻两个元素相等则消除 直到最后字符串消除不了了 输出消消乐后字符串的长度 若遇到非大小写字母
  • layui实现左侧菜单点击右侧内容区显示

    layui这个前端框架对后端人员来说提供了极大的方便 很大一部分同学在刚接触到这个框架就迫不及待的要动手实践了 刚好最近需要开发一个后台管理项目 就拿layui来进行学习和实践 还没有接触到的同学可以先通读一遍文档 这样在遇到问题的时候才能
  • 用C语言打造 时间戳转换成北京时间格式

    本函数适用于SMT32F10x GD32F10x的RTC时间戳转换成有效年月日和时间 uint8 t const Year a 12 31 28 31 30 31 30 31 31 30 31 30 31 uint8 t const Yea
  • 银联支付开发、使用的一些总结

    现在的网页支付 PC和微信H5 和app支付 用的比较多的是微信支付 银联支付和支付宝支付 其余的是这些支付的第三方支付 我目前了解的只有这么多 我目前做了银联支付和微信支付 这里说一些银联支付的开发的一些介绍吧 根据我们公司的应用经验 银