【其他】【支付】【1】M-pesa(非洲肯尼亚的支付方式)
时间:2019-08-17
本文章向大家介绍【其他】【支付】【1】M-pesa(非洲肯尼亚的支付方式),主要包括【其他】【支付】【1】M-pesa(非洲肯尼亚的支付方式)使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
前言:
M-pesa:肯尼亚移动运营商Safaricom推出的手机银行业务。是依托于手机SIM卡进行支付的。
官网:https://www.safaricom.co.ke/dealers/login.php
开发者网站:https://developer.safaricom.co.ke/docs#going-live
正文:
业务背景:公司需要在官网上加一个注册为经销商的功能,其中有一个环节就是需要用户支付一定的金钱。业务范围是在肯尼亚。
流程:需要先在肯尼亚那边注册一个商户账号,那边审批也要一定的时间,所以要提早去申请。通过之后,我们可以拿到商户编号,密钥等信息。后台需要写两个接口,一个是发送支付请求给Mpesa,一个是获取Mpesa的支付状态。前端需要在用户点击支付之后调用后台的支付接口,然后不断地请求后台的支付状态接口,直到获取到状态为止。用户那边的表现形式为,手机上会受到一个支付的弹窗,会有金额数和商户名称,用户输入密码即可完成支付。如果用户超时支付的话,是不会支付成功的。弹窗关闭后,用户在手机上是无法主动再次调出弹窗的,也没有相关的短信。
代码:
支付相关:
//支付相关 @Service public class PaymentService { public static Logger logger = Logger.getLogger(PaymentService.class.getName()); @Autowired private IDistributorDao distributorDao; //更新经销商信息 @Autowired private IPaymentBillDao paymentBillDao; //记录订单流水 /** 给前端显示的商户信息 * paymentVo:用户名,支付手机号等 * PaybillVo:返回给前端的信息,订单号,金额等 */ public ResultComplete<PaybillVo> getPaybill(PaymentVo paymentVo) { logger.info("getPaybill begin; info:%s" + paymentVo.toString()); PaybillVo info = new PaybillVo(); info.setPaybill(MpesaConfig.COMPANY_NAME); //商户名称(与用户弹窗内的显示一致) info.setPayAmount(BaseView.PAY_AMOUNT); //金额(货币类型为肯尼亚先令) info.setOrderNo(this.getOrderNo()); //获取订单号 //处理支付流水。。。记录相关信息到支付流水表 return new ResultComplete<>(info); } //支付接口 public ResultComplete<String> payment(String payPhone, String orderNo) throws IOException { String amount = BaseView.PAY_AMOUNT; logger.info(String.format("payment begin; params: payPhone:%s, orderNo:%s", amount, payPhone, orderNo)); //调用M-pesa接口 String payInfo = Mpesa.STKPushSimulation(amount, payPhone, orderNo); //TODO test 测试的时候,因为我在国内,没有办法支付,所以把数据写死了。 // 由于是改造旧项目,工期比较紧,所以没有进行配置,直接是注释掉了,大家有时间可以优化一下,这里仅做参考 // String payInfo = "{" // + "\"MerchantRequestID\":\"6809-2590977-1\"," // + "\"CheckoutRequestID\":\"test\"," // + "\"ResponseCode\": \"0\"" // + "}"; Gson gson = new Gson(); Map map = gson.fromJson(payInfo, Map.class); String responseCode = (String) map.get("ResponseCode"); String checkoutRequestId = (String) map.get("CheckoutRequestID"); if (responseCode == null || Integer.parseInt(responseCode) != 0) { logger.error("payment request fail"); String payResultCode = responseCode; String payResultDesc = (String) map.get("CustomerMessage"); if (StringUtils.isBlank(responseCode)) { payResultCode = (String) map.get("errorCode"); payResultDesc = (String) map.get("errorMessage"); } //支付请求失败时更新支付流水表。。。 return new ResultComplete<>(false, 101, "支付请求失败", null); } //由于是旧项目的缘故,我用的session进行校验。其实不是很方便,可改为存到Redis HttpSession session = HttpHelper.getSession(); session.setAttribute(BaseView.SESSION_PAY_KEY + orderNo, payInfo); session.setMaxInactiveInterval(0); //支付请求成功时更新支付流水表。。。 logger.info("payment end"); return new ResultComplete<>(null); } //前端调用支付状态 public ResultComplete<String> getPaymentStatus(String orderNo) throws IOException { logger.info(String.format("getPaymentStatus begin; params: orderNo:%s", orderNo)); //TODO test String checkoutRequestId = getCheckoutRequestId(orderNo); //获取支付请求流水号 if (StringUtils.isBlank(checkoutRequestId)) { return new ResultComplete<>(false, 101, "未获取支付请求信息", null); } String statusInfo = Mpesa.STKPushTransactionStatus(checkoutRequestId); // String statusInfo = "{" // + "\"MerchantRequestID\":\"6809-2590977-1\"," // + "\"CheckoutRequestID\":\"test\"," // + "\"ResponseCode\": \"0\"," // + "\"ResultCode\": \"0\"," // + "\"ResultDesc\": \"Success\"" // + "}"; Gson gson = new Gson(); Map map = gson.fromJson(statusInfo, Map.class); String responseCode = (String) map.get("ResponseCode"); String resultCode = (String) map.get("ResultCode"); String resultDesc = (String) map.get("ResultDesc"); if (responseCode == null || Integer.parseInt(responseCode) != 0 || resultCode == null || Integer.parseInt(resultCode) != 0) { logger.error("payment status fail"); String errorCode = (String) map.get("errorCode"); String errorMessage = (String) map.get("errorMessage"); if (errorCode != null && errorCode.equals("500.001.1001")) { return new ResultComplete<>(true, 201, "支付正在处理中", null); } String payResultCode = StringUtils.isNotBlank(resultCode) ? resultCode : errorCode; String payResultDesc = StringUtils.isNotBlank(resultDesc) ? resultDesc : errorMessage; //更新流水 return new ResultComplete<>(false, 102, "支付失败", null); } HttpSession session = HttpHelper.getSession(); session.setAttribute(BaseView.SESSION_PAY_STATUS_KEY + orderNo, statusInfo); session.setMaxInactiveInterval(0); //更新流水 logger.info("getPaymentStatus end"); return new ResultComplete<>(null); } //获取订单号(我定义的是PAY-20190817-000001这样,可自己视情况而定) private String getOrderNo() { String nowDate = CalendarUtil.DtoSYmd(new Date()); String lastOrderNo = this.paymentBillDao.getLastOrderNo(nowDate); String orderNo = "PAY-" + nowDate + "-000001"; if (StringUtils.isNotBlank(lastOrderNo)) { String numberFmt = lastOrderNo.substring(lastOrderNo.length() - 6); //取最大编号后6位; String end = String.format("%06d", Integer.parseInt(numberFmt) + 1); orderNo = "PAY-" + nowDate + "-" + end; } return orderNo; } //获取支付请求流水号 private String getCheckoutRequestId(String orderNo) { HttpSession session = HttpHelper.getSession(); String payInfo = String.valueOf(session.getAttribute(BaseView.SESSION_PAY_KEY + orderNo)); if (StringUtils.isBlank(payInfo) || payInfo.equals("null")) { return ""; } Gson gson = new Gson(); Map map = gson.fromJson(payInfo, Map.class); String responseCode = (String) map.get("ResponseCode"); if (responseCode == null || Integer.parseInt(responseCode) != 0) { return ""; } return (String) map.get("CheckoutRequestID"); } }
原文地址:https://www.cnblogs.com/huashengweilong/p/11370656.html
- 【Go 语言社区】Golang 语言再谈常量
- 【Go 语言社区】HTML5 Canvas+JS控制电脑或手机上的摄像头实例
- MySQL Profile在5.7的简单测试(r10笔记第50天)
- 【Go 语言社区】Golang中interface判断nil问题
- 有趣的rownum测试(r10笔记第49天)
- 【Go 语言社区】关于Golang 数据缓存到redis内存数据库遇到的问题
- go中的读写锁RWMutex
- Centos7.4 版本环境下安装Mysql5.7操作记录
- 你必须知道的23个最有用的Elasticseaerch检索技巧
- Elasticsearch Jest实战深入详解
- 在oracle中计算时间差
- 【Go 语言社区】GO中怎么处理URL编码?
- C语言库函数rename
- Oracle 12c DG新特性Far Sync(r10笔记第67天)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- ValueError: too many values to unpack (expected 2)
- VMware15更新后克隆Centos7发现网卡起不来了
- 基于SSH的医院在线挂号
- Linux-远程拷贝(scp命令)
- Kettle使用JavaScript代码处理数据
- Hadoop入门---(wordcount)统计单词出现的次数
- JS去除字符串的空格
- insertionSoft(插入排序) 2.1-1 And 重写insertionSoft 2.1-2
- Swagger-Springboot-mybatis-mysql
- Python+java+websocket+SpringMVC实时监控数据库中的表
- 基于Java图形界面的IPV4与网址的地址解析器
- 如何在千里之外能访问自己的电脑?(FRP)
- 三分钟Docker-镜像、容器实战篇
- 看懂今天这个!你就是个真正的javaer!
- 猿进化系列7——一文搞懂IO