【其他】【支付】【1】M-pesa(非洲肯尼亚的支付方式)

本文介绍了M-Pesa,一种在肯尼亚流行的手机支付方式,由Safaricom运营。讨论了如何注册商户账号并使用其API进行支付集成。在后台需要实现三个接口,前端则负责调起支付并监测支付状态。用户支付过程涉及手机弹窗确认,超时后需重新触发。
摘要由CSDN通过智能技术生成

前言:

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:用户名,支付手机号等(传过来的手机号格式为 区号手机号,前面不要加+号,中间不要加横杠。254777777777)
     *  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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值