【其他】【支付】【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