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

前言:php

M-pesa:肯尼亞移動運營商Safaricom推出的手機銀行業務。是依託於手機SIM卡進行支付的。前端

官網:https://www.safaricom.co.ke/dealers/login.phpjava

開發者網站:https://developer.safaricom.co.ke/docs#going-liveandroid

正文:git

業務背景:公司須要在官網上加一個註冊爲經銷商的功能,其中有一個環節就是須要用戶支付必定的金錢。業務範圍是在肯尼亞。github

流程:apache

須要先在肯尼亞那邊註冊一個商戶帳號,那邊審批也要必定的時間,因此要提前去申請。經過以後,咱們能夠拿到商戶編號,密鑰等信息,它是有正式接口和測試接口的,能夠先用測試接口調試。json

後臺須要寫三個接口,一個是給前端的信息,一個是發送支付請求給Mpesa,一個是獲取Mpesa的支付狀態。api

前端須要在用戶點擊支付以後調用後臺的支付接口,而後不斷地請求後臺的支付狀態接口,直到獲取到狀態爲止。tomcat

用戶那邊的表現形式爲,手機上會收到一個支付的彈窗,會有金額數和商戶名稱,用戶輸入密碼便可完成支付。用戶支付超時,彈窗還在,此時輸入支付密碼是不會支付成功的。在彈窗關閉後,用戶在手機上是沒法主動再次調出彈窗的,也沒有相關的短信。

代碼:

//支付相關
@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 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");
    }
}

 MpesaConfig(能夠考慮寫在配置文件)

public class MpesaConfig {

    /**
     * Connection timeout duration
     */
    public static final int CONNECT_TIMEOUT = 60 * 1000;
    /**
     * Connection Read timeout duration
     */
    public static final int READ_TIMEOUT = 60 * 1000;
    /**
     * Connection write timeout duration
     */
    public static final int WRITE_TIMEOUT = 60 * 1000;
    
    /**
     * global topic to receive app wide push notifications
     */
    public static final String TOPIC_GLOBAL = "global";

    // broadcast receiver intent filters
    public static final String REGISTRATION_COMPLETE = "registrationComplete";
    public static final String PUSH_NOTIFICATION = "pushNotification";

    // id to handle the notification in the notification tray
    public static final int NOTIFICATION_ID = 100;
    public static final int NOTIFICATION_ID_BIG_IMAGE = 101;
    public static final String SHARED_PREF = "ah_firebase";

    //STKPush Properties
    //public static final String BASE_URL = "https://sandbox.safaricom.co.ke"; //測試用的Base URL
    public static final String BASE_URL = "https://api.safaricom.co.ke"; //Base URL
    public static final String BUSINESS_SHORT_CODE = ""; //商戶編號
    public static final String PASSKEY = "";
    public static final String TRANSACTION_TYPE = "CustomerPayBillOnline";
    public static final String PARTYB = ""; //收款賬號(通常與商戶編號一致)
    // 付款回調url ,驗證付款發送請求是否成功(非必須)
    public static final String CALLBACKURL = "http://www.*.com/pay/request/info?orderNo=";

    //密鑰
    public static final String CONSUMER_KEY = "";
    //secret
    public static final String CONSUMER_SECRET = "";
    
    public static String AccessToken = null; //根據密鑰和secret得到,是有有效期的

}

Mpesa:

package com.mpesa;

import java.io.IOException;
import java.util.Base64;

import org.apache.log4j.Logger;

import com.google.gson.Gson;
import com.mpesa.STKPush;
import com.mpesa.STKPushTransactionStatusBean;

import net.sf.json.JSONArray;
import net.sf.json.JSONException;
import net.sf.json.JSONObject;

import com.mpesa.MpesaUtils;


import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;

public class Mpesa {
    public static Logger logger = Logger.getLogger(Mpesa.class.getName());
    
    static String appKey = MpesaConfig.CONSUMER_KEY;
    static String appSecret = MpesaConfig.CONSUMER_SECRET;
//    static String accessToken;
    
//    public Mpesa(String app_key, String app_secret) { 
//        appKey = app_key;
//        appSecret = app_secret; 
//    }

    /**
     * 獲取 AccessToken
     *
     * @return
     * @throws IOException
     */
    public static String authenticate() throws IOException {
        logger.info("authenticate begin");
        String app_key = appKey;
        String app_secret = appSecret;
        String appKeySecret = app_key + ":" + app_secret;
        byte[] bytes = appKeySecret.getBytes("ISO-8859-1");
        String encoded = Base64.getEncoder().encodeToString(bytes);

        OkHttpClient client = new OkHttpClient();

        Request request = new Request.Builder()
                .url(MpesaConfig.BASE_URL + "/oauth/v1/generate?grant_type=client_credentials").get()
                .addHeader("authorization", "Basic " + encoded).addHeader("cache-control", "no-cache").build();

        Response response = client.newCall(request).execute();
        JSONObject jsonObject = null;
        String accessToken = null;
        try {
            // jsonObject = new JSONObject(response.body().string());
            ResponseBody ss = response.body();
            String info = ss.string();
            logger.info("pay authenticate; info:%s" + info);

            jsonObject = JSONObject.fromObject(info);
            accessToken = jsonObject.getString("access_token");
            MpesaConfig.AccessToken = accessToken;
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return accessToken;
    }

    public String C2BSimulation(String shortCode, String commandID, String amount, String MSISDN, String billRefNumber)
            throws IOException {    
        if (MpesaConfig.AccessToken == null) {
            MpesaConfig.AccessToken = authenticate();
        }
        JSONArray jsonArray = new JSONArray();
        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put("ShortCode", shortCode);
            jsonObject.put("CommandID", commandID);
            jsonObject.put("Amount", amount);
            jsonObject.put("Msisdn", MSISDN);
            jsonObject.put("BillRefNumber", billRefNumber);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        jsonArray.add(jsonObject);

        String requestJson = jsonArray.toString().replaceAll("[\\[\\]]", "");
        logger.info("C2BSimulation requestJson:" + requestJson);
        
        OkHttpClient client = new OkHttpClient();
        MediaType mediaType = MediaType.parse("application/json");
        RequestBody body = RequestBody.create(mediaType, requestJson);
        Request request = new Request.Builder().url(MpesaConfig.BASE_URL + "/safaricom/c2b/v1/simulate").post(body)
                .addHeader("content-type", "application/json").addHeader("authorization", "Bearer " + MpesaConfig.AccessToken)
                .addHeader("cache-control", "no-cache").build();

        Response response = client.newCall(request).execute();
        String info = response.body().string();
        logger.info("C2BSimulation end; info:" + info);

        return info;
    }

    public String B2CRequest(String initiatorName, String securityCredential, String commandID, String amount,
            String partyA, String partyB, String remarks, String queueTimeOutURL, String resultURL, String occassion)
            throws IOException {
        if (MpesaConfig.AccessToken == null) {
            MpesaConfig.AccessToken = authenticate();
        }
        JSONArray jsonArray = new JSONArray();
        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put("InitiatorName", initiatorName);
            jsonObject.put("SecurityCredential", securityCredential);
            jsonObject.put("CommandID", commandID);
            jsonObject.put("Amount", amount);
            jsonObject.put("PartyA", partyA);
            jsonObject.put("PartyB", partyB);
            jsonObject.put("Remarks", remarks);
            jsonObject.put("QueueTimeOutURL", queueTimeOutURL);
            jsonObject.put("ResultURL", resultURL);
            jsonObject.put("Occassion", occassion);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        jsonArray.add(jsonObject);
        String requestJson = jsonArray.toString().replaceAll("[\\[\\]]", "");
        logger.info("B2CRequest requestJson:" + requestJson);
        
        OkHttpClient client = new OkHttpClient();
        MediaType mediaType = MediaType.parse("application/json");
        RequestBody body = RequestBody.create(mediaType, requestJson);
        Request request = new Request.Builder().url(MpesaConfig.BASE_URL + "/mpesa/b2c/v1/paymentrequest").post(body)
                .addHeader("content-type", "application/json").addHeader("authorization", "Bearer " + MpesaConfig.AccessToken)
                .addHeader("cache-control", "no-cache").build();

        Response response = client.newCall(request).execute();
        String info = response.body().string();
        logger.info("B2CRequest end; info:" + info);
        
        return info;
    }

    public String B2BRequest(String initiatorName, String accountReference, String securityCredential, String commandID,
            String senderIdentifierType, String receiverIdentifierType, float amount, String partyA, String partyB,
            String remarks, String queueTimeOutURL, String resultURL, String occassion) throws IOException {
        if (MpesaConfig.AccessToken == null) {
            MpesaConfig.AccessToken = authenticate();
        }
        
        JSONArray jsonArray = new JSONArray();
        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put("Initiator", initiatorName);
            jsonObject.put("SecurityCredential", securityCredential);
            jsonObject.put("CommandID", commandID);
            jsonObject.put("SenderIdentifierType", senderIdentifierType);
            jsonObject.put("RecieverIdentifierType", receiverIdentifierType);
            jsonObject.put("Amount", amount);
            jsonObject.put("PartyA", partyA);
            jsonObject.put("PartyB", partyB);
            jsonObject.put("Remarks", remarks);
            jsonObject.put("AccountReference", accountReference);
            jsonObject.put("QueueTimeOutURL", queueTimeOutURL);
            jsonObject.put("ResultURL", resultURL);
        } catch (JSONException e) {
            e.printStackTrace();
        }

        jsonArray.add(jsonObject);

        String requestJson = jsonArray.toString().replaceAll("[\\[\\]]", "");
        logger.info("B2BRequest requestJson:" + requestJson);

        OkHttpClient client = new OkHttpClient();
        MediaType mediaType = MediaType.parse("application/json");
        RequestBody body = RequestBody.create(mediaType, requestJson);
        Request request = new Request.Builder().url(MpesaConfig.BASE_URL + "/safaricom/b2b/v1/paymentrequest")
                .post(body).addHeader("content-type", "application/json")
                .addHeader("authorization", "Bearer " + MpesaConfig.AccessToken).addHeader("cache-control", "no-cache")

                .build();

        Response response = client.newCall(request).execute();
        String info = response.body().string();
        logger.info("B2BRequest end; info:" + info);
        
        return info;

    }

    /**
     * 
     * @param amount
     *            金額
     * @param phoneNumber
     *            手機號,
     * @param orderid
     *            訂單id
     * @return
     * @throws IOException
     */
    public static String STKPushSimulation(String amount, String phoneNumber, String orderid) throws IOException {
        logger.info(String.format("pay request begin; amount:%s,phoneNumber:%s,orderid:%s", amount, phoneNumber, orderid));
        logger.info("MpesaConfig.AccessToken:" + MpesaConfig.AccessToken);
        MpesaConfig.AccessToken = authenticate();

        String timestamp = MpesaUtils.getTimestamp();
        STKPush stkPush = new STKPush(MpesaConfig.BUSINESS_SHORT_CODE,
                MpesaUtils.getPassword(MpesaConfig.BUSINESS_SHORT_CODE, MpesaConfig.PASSKEY, timestamp), timestamp,
                MpesaConfig.TRANSACTION_TYPE, String.valueOf(amount), MpesaUtils.sanitizePhoneNumber(phoneNumber),
                MpesaConfig.PARTYB, MpesaUtils.sanitizePhoneNumber(phoneNumber),
                MpesaConfig.CALLBACKURL + orderid + "&phone_number=" + phoneNumber, "BFSumaOnLine", "test");
        Gson gson = new Gson();
        String requestJson = gson.toJson(stkPush);
        OkHttpClient client = new OkHttpClient();
        String url = MpesaConfig.BASE_URL + "/mpesa/stkpush/v1/processrequest";
        MediaType mediaType = MediaType.parse("application/json");
        RequestBody body = RequestBody.create(mediaType, requestJson);
        Request request = new Request.Builder().url(url).post(body).addHeader("content-type", "application/json")
                .addHeader("authorization", "Bearer " + MpesaConfig.AccessToken).addHeader("cache-control", "no-cache").build();

        Response response = client.newCall(request).execute();
        String info = response.body().string();
        logger.info("pay request end; info:" + info);
        
        return info;
    }

    /**
     * 查詢在線訂單狀態
     * 
     * @param checkoutRequestID
     *            在線付款提交成功後返回 checkoutRequestID
     * @return
     * @throws IOException
     */
    public static String STKPushTransactionStatus(String checkoutRequestID) throws IOException {
        logger.info(String.format("pay status begin; checkoutRequestID:%s", checkoutRequestID));
        
        if (MpesaConfig.AccessToken == null) {
            MpesaConfig.AccessToken = authenticate();
        }
        String timestamp = MpesaUtils.getTimestamp();
        String password = MpesaUtils.getPassword(MpesaConfig.BUSINESS_SHORT_CODE, MpesaConfig.PASSKEY, timestamp);
        STKPushTransactionStatusBean bean = new STKPushTransactionStatusBean(MpesaConfig.BUSINESS_SHORT_CODE, password,
                timestamp, checkoutRequestID);
        Gson gson = new Gson();
        String requestJson = gson.toJson(bean);
        OkHttpClient client = new OkHttpClient();
        MediaType mediaType = MediaType.parse("application/json");
        RequestBody body = RequestBody.create(mediaType, requestJson);
        Request request = new Request.Builder().url(MpesaConfig.BASE_URL + "/mpesa/stkpushquery/v1/query").post(body)
                .addHeader("authorization", "Bearer " + MpesaConfig.AccessToken).addHeader("content-type", "application/json")
                .build();
        Response response = client.newCall(request).execute();
        String info = response.body().string();
        logger.info("pay status end; info:" + info);

        return info;

    }

    public String reversal(String initiator, String securityCredential, String commandID, String transactionID,
            String amount, String receiverParty, String recieverIdentifierType, String resultURL,
            String queueTimeOutURL, String remarks, String ocassion) throws IOException {
        if (MpesaConfig.AccessToken == null) {
            MpesaConfig.AccessToken = authenticate();
        }
        JSONArray jsonArray = new JSONArray();
        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put("Initiator", initiator);
            jsonObject.put("SecurityCredential", securityCredential);
            jsonObject.put("CommandID", commandID);
            jsonObject.put("TransactionID", transactionID);
            jsonObject.put("Amount", amount);
            jsonObject.put("ReceiverParty", receiverParty);
            jsonObject.put("RecieverIdentifierType", recieverIdentifierType);
            jsonObject.put("QueueTimeOutURL", queueTimeOutURL);
            jsonObject.put("ResultURL", resultURL);
            jsonObject.put("Remarks", remarks);
            jsonObject.put("Occasion", ocassion);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        jsonArray.add(jsonObject);

        String requestJson = jsonArray.toString().replaceAll("[\\[\\]]", "");
        logger.info("reversal requestJson:" + requestJson);

        OkHttpClient client = new OkHttpClient();
        MediaType mediaType = MediaType.parse("application/json");
        RequestBody body = RequestBody.create(mediaType, requestJson);
        Request request = new Request.Builder().url(MpesaConfig.BASE_URL + "/safaricom/reversal/v1/request").post(body)
                .addHeader("content-type", "application/json")
                .addHeader("authorization", "Bearer xNA3e9KhKQ8qkdTxJJo7IDGkpFNV")
                .addHeader("cache-control", "no-cache").build();

        Response response = client.newCall(request).execute();
        String info = response.body().string();
        logger.info("reversal end; info:" + info);

        return info;
    }

    public String balanceInquiry(String initiator, String commandID, String securityCredential, String partyA,
            String identifierType, String remarks, String queueTimeOutURL, String resultURL) throws IOException {
        if (MpesaConfig.AccessToken == null) {
            MpesaConfig.AccessToken = authenticate();
        }
        JSONArray jsonArray = new JSONArray();
        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put("Initiator", initiator);
            jsonObject.put("SecurityCredential", securityCredential);
            jsonObject.put("CommandID", commandID);
            jsonObject.put("PartyA", partyA);
            jsonObject.put("IdentifierType", identifierType);
            jsonObject.put("Remarks", remarks);
            jsonObject.put("QueueTimeOutURL", queueTimeOutURL);
            jsonObject.put("ResultURL", resultURL);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        jsonArray.add(jsonObject);

        String requestJson = jsonArray.toString().replaceAll("[\\[\\]]", "");
        logger.info("balanceInquiry requestJson:" + requestJson);

        OkHttpClient client = new OkHttpClient();
        MediaType mediaType = MediaType.parse("application/json");
        RequestBody body = RequestBody.create(mediaType, requestJson);
        Request request = new Request.Builder().url(MpesaConfig.BASE_URL + "/safaricom/accountbalance/v1/query")
                .post(body).addHeader("content-type", "application/json")
                .addHeader("authorization", "Bearer fwu89P2Jf6MB1A2VJoouPg0BFHFM")
                .addHeader("cache-control", "no-cache")
                .addHeader("postman-token", "2aa448be-7d56-a796-065f-b378ede8b136").build();

        Response response = client.newCall(request).execute();
        String info = response.body().string();
        logger.info("balanceInquiry end; info:" + info);
        
        return info;
    }

    public String registerURL(String shortCode, String responseType, String confirmationURL, String validationURL)
            throws IOException {
        if (MpesaConfig.AccessToken == null) {
            MpesaConfig.AccessToken = authenticate();
        }
        JSONArray jsonArray = new JSONArray();
        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put("ShortCode", shortCode);
            jsonObject.put("ResponseType", responseType);
            jsonObject.put("ConfirmationURL", confirmationURL);
            jsonObject.put("ValidationURL", validationURL);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        jsonArray.add(jsonObject);
        String requestJson = jsonArray.toString().replaceAll("[\\[\\]]", "");
        logger.info("registerURL requestJson:" + requestJson);
        
        OkHttpClient client = new OkHttpClient();
        MediaType mediaType = MediaType.parse("application/json");
        RequestBody body = RequestBody.create(mediaType, requestJson);
        Request request = new Request.Builder().url(MpesaConfig.BASE_URL + "/mpesa/c2b/v1/registerurl").post(body)
                .addHeader("content-type", "application/json").addHeader("authorization", "Bearer " + MpesaConfig.AccessToken)
                .addHeader("cache-control", "no-cache").build();

        Response response = client.newCall(request).execute();
        String info = response.body().string();
        logger.info("registerURL end; info:" + info);
        
        return info;
    }
}

MpesaUtils:

package com.mpesa;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

import org.apache.tomcat.util.codec.binary.Base64;

/**
 * Created by miles on 23/11/2017.
 * Taken from https://github.com/bdhobare/mpesa-android-sdk
 */

public class MpesaUtils {

    public static String getTimestamp() {
        return new SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).format(new Date());
    }

    public static String sanitizePhoneNumber(String phone) {

        if (phone.equals("")) {
            return "";
        }

        if (phone.length() < 11 & phone.startsWith("0")) {
            String p = phone.replaceFirst("^0", "254");
            return p;
        }
        if (phone.length() == 13 && phone.startsWith("+")) {
            String p = phone.replaceFirst("^+", "");
            return p;
        }
        return phone;
    }

    public static String getPassword(String businessShortCode, String passkey, String timestamp) {
        String str = businessShortCode + passkey + timestamp;
        //encode the password to Base64
//        return Base64.encodeToString(str.getBytes(), Base64.NO_WRAP);
        return Base64.encodeBase64String(str.getBytes());

    }
}

STKPush

package com.mpesa;


/**
 * 在線付款請求參數
 * 
 */

public class STKPush {
/*    {
          "BusinessShortCode": "",
          "Password": "",
          "Timestamp": "",
          "TransactionType": "CustomerPayBillOnline",
          "Amount": "",
          "PartyA": "",
          "PartyB": "",
          "PhoneNumber": "",
          "CallBackURL": "",
          "AccountReference": "",
          "TransactionDesc": ""
        }
    
    */
    
    //收款方短碼   組織短代碼
    private String BusinessShortCode;
   // 在線支付密碼  base64.encode(Shortcode + Passkey + Timestamp),passkey爲在線支付密碼
    private String Password;
    //時間戳  YYYYMMDDHHmmss
    private String Timestamp;
    // 付款類型   CustomerPayBillOnline(默認)    CustomerBuyGoodsOnline
    private String TransactionType;
//        金額  整數
    private String Amount;
    //  寄錢電話號碼    付款方
    private String PartyA;
    // 收款方  ,與businessShortCode 相同
    private String PartyB;
    // 付款號碼 2547***  12位    用於接收STK推送,與PartA 相同
    private String PhoneNumber;
    // 付款請求發送成功,mpesa服務器 發送消息付款推送到客戶手機成功後,調用此連接 通知用戶(本身服務器)
    private String CallBackURL;
    //帳戶備註  小於12個字符(字母和數字的任何組合)
    private String AccountReference;
    // 其餘信息  小於13 字符,
    private String TransactionDesc;

    public STKPush(String businessShortCode, String password, String timestamp, String transactionType,
                   String amount, String partyA, String partyB, String phoneNumber, String callBackURL,
                   String accountReference, String transactionDesc) {
        this.BusinessShortCode = businessShortCode;
        this.Password = password;
        this.Timestamp = timestamp;
        this.TransactionType = transactionType;
        this.Amount = amount;
        this.PartyA = partyA;
        this.PartyB = partyB;
        this.PhoneNumber = phoneNumber;
        this.CallBackURL = callBackURL;
        this.AccountReference = accountReference;
        this.TransactionDesc = transactionDesc;
    }
}

STKPushTransactionStatusBean

package com.mpesa;
/**
 * 在線付款查詢請求倉庫
 * @author zzc
 *
 */
public class STKPushTransactionStatusBean {
    /*{
        "BusinessShortCode": " " ,  收款方斷代碼
        "Password": " ",  密鑰
        "Timestamp": " ", 時間戳
        "CheckoutRequestID": " " 在線付款id
}*/
    private String BusinessShortCode;
    private String Password;
    private String Timestamp;
    private String CheckoutRequestID;
    public String getBusinessShortCode() {
        return BusinessShortCode;
    }
    public void setBusinessShortCode(String businessShortCode) {
        BusinessShortCode = businessShortCode;
    }
    public String getPassword() {
        return Password;
    }
    public void setPassword(String password) {
        Password = password;
    }
    public String getTimestamp() {
        return Timestamp;
    }
    public void setTimestamp(String timestamp) {
        Timestamp = timestamp;
    }
    public String getCheckoutRequestID() {
        return CheckoutRequestID;
    }
    public void setCheckoutRequestID(String checkoutRequestID) {
        CheckoutRequestID = checkoutRequestID;
    }
    public STKPushTransactionStatusBean(String businessShortCode, String password, String timestamp,
            String checkoutRequestID) {
        BusinessShortCode = businessShortCode;
        Password = password;
        Timestamp = timestamp;
        CheckoutRequestID = checkoutRequestID;
    }
    
    
}
相關文章
相關標籤/搜索