M-PESA API 在线结账 Java / Android 如何
在 M-PESA API 发布之后,您需要创建一个 Web 服务客户端来执行 SOAP 请求并处理来自 API 调用的响应
我将为致力于集成现有 Java 应用程序的 Java 开发人员提供快速入门指南。
先决条件
JDK 7 或最新版本
说明步骤
从 safaricom 网站下载完整的 M-PESA API 指南
通读开发者指南 C2B_OnlineCheckout_V2.0.doc
生成 JAX-WS 工件。您的客户端将使用此 API 访问已发布的 Web 服务。
生成 JAX-WS 工件
我们将使用 wsimport 工具来解析签出的 WSDL 文件并生成所需的文件。
mkdir src
wsimport -XadditionalHeaders -clientjar safaricom-lipanampesa-onlinecheckout-wsdl.jar -s src -p ke.co.makara.integration.mpesa.lnmocheckout http://safaricom.co.ke/mpesa_online/lnmo_checkout_server.php?wsdl
检查 wsimport --help 以了解上述选项;
请注意生成的工件的包目录。 WSDL 托管在 lipa na M-PESA 端点上。为此,我们使用生产 URL。 -clientjar 选项,Java 7 中的新选项,简化了事情。否则,您将不得不使用 -wsdllocation /META-INF/wsdl/Checkout.wsdl 选项。并在之后复制 META-INF 中的 wsdl 以减轻 Java 6 wsimport 限制。请参阅下面的限制。
可选择创建源 jar
在 src 文件夹中运行命令。
jar cvf safaricom-lipanampesa-onlinecheckout-source-wsdl.jar ke/
接下来,我们使用 Web 服务工件从 Web 服务客户端调用 Web 服务。
生成的工件
服务端点接口 (SEI) - LNMOPortType.java
服务类 - LnmoCheckoutService.java
如果 WSDL 中存在 wsdl:fault,则 Exception 类
从模式类型映射的 Java 类,例如 ProcessCheckOutRequest.java
如果 WSDL 中存在 wsdl:message,则异步响应 bean 例如 ProcessCheckOutResponse.java
网络服务客户端
接下来,我们使用 Web 服务工件从 Web 服务客户端调用 Web 服务。
这可以是从前端调用的 servlet。超出范围。为简单起见,我将创建一个 java 类 ~ 一个独立的控制台
创建一个 java 类说 LNMOCheckoutTester
在 Java 客户端应用程序中,创建 LnmoCheckoutService 服务的实例
LnmoCheckoutService lnmoCheckoutService = new LnmoCheckoutService(); // lina na mpesa online checkout instance
Service 类将在构建期间创建。
使用 getLnmoCheckout() 方法从服务中获取到服务的代理
LNMOPortType soap = lnmoCheckoutService.getLnmoCheckout();
端口携带协议绑定和服务端点地址信息。
配置服务端点
在 javax.xml.ws.BindingProvider 接口上配置请求上下文属性
((BindingProvider)soap).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, url + "lnmo_checkout_server.php"); // specifying this property on the RequestContext
JAX-WS 提供对服务端点操作的动态调用的支持。
我将 Web 服务 url 存储在数据库中,并将 wsdl 端点附加到该 url。这会在运行时配置端点。
撰写我们的客户请求 M-PESA 结帐消息
这是消息负载
ProcessCheckOutRequest checkOutRequest = objFactory.createProcessCheckOutRequest();
checkOutRequest.setMERCHANTTRANSACTIONID("54635469064");
checkOutRequest.setREFERENCEID("TD346534GH");
checkOutRequest.setAMOUNT(13300);
checkOutRequest.setMSISDN("0721XXXXXX");
checkOutRequest.setENCPARAMS("");
checkOutRequest.setCALLBACKURL("https://makara.co.ke:8443/odt/checkout");
checkOutRequest.setCALLBACKMETHOD("GET");
checkOutRequest.setTIMESTAMP(String.valueOf(date.getTime()));
配置请求标头
按照 Safaricom 文档中的业务规则构建密码。见附件代码。
对于字符串的merchantId、String passkey、Date requestTimeStamp;将连接的字符串转换为字节,对字节进行哈希处理以获得任意二进制数据并将二进制数据转换为字符串使用 base64
CheckOutHeader requestHeader = objFactory.createCheckOutHeader();
requestHeader.setMERCHANTID(MERCHANT_ID);
Date timestamp = new Date();
String encryptedPassword = getEncryptedPassword(MERCHANT_ID, PASSKEY, timestamp);
requestHeader.setPASSWORD(encryptedPassword.toUpperCase());
requestHeader.setTIMESTAMP(String.valueOf(timestamp.getTime()));
使用基于 dispatch stab 的客户端调用服务端点
soap.processCheckOut(checkOutRequest, requestHeader);
根据您的业务需求处理来自服务的响应消息
ProcessCheckOutResponse checkOutResponse = new ProcessCheckOutResponse();
checkOutResponse.getRETURNCODE();
checkOutResponse.getDESCRIPTION();
checkOutResponse.getTRXID();
checkOutResponse.getCUSTMSG();
跟踪 SOAP 流量
调试 Web 服务应用程序的一个常见步骤是检查请求和响应 SOAP 消息
配置客户端以使用 JAX-WS 转储请求和响应
System.setProperty("com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump", "true");
源代码
/*
* LNMOCheckoutTester.java
*
* Nov 20, 2016 Joseph Makara - Created File to tester Lina Na M-PESA Online checkout
*
*
*/
package testMe;
import java.io.*;
import java.security.*;
import java.util.*;
import javax.net.ssl.*;
import javax.xml.ws.*;
import ke.co.makara.integration.mpesa.lnmocheckout.*;
import org.apache.commons.codec.binary.*;
/**
* @author Joseph Makara
*
*/
public class LNMOCheckoutTester {
private static final String PASSKEY = "234fdsghfsg5654dgfhgf6dsfdsafsd43dgfhdgfdgfh74567";
private static final String MERCHANT_ID = "678fsgd54354";
private static final String REFERENCE_ID = "";
private static final String ENDPOINT_URL = "https://safaricom.co.ke/mpesa_online/";
private static final String CALLBACK_URL = "https://makara.co.ke:8443/odt/checkout";
private static final String CALLBACK_METHOD = "GET";
static {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
if (hostname.equals("safaricom.co.ke")) return true;
return false;
}
});
}
/**
* @param args
*/
public static void main(String[] args) {
LNMOPortType soap = outBoundLNMOCheckout(ENDPOINT_URL);
ObjectFactory objFactory = new ObjectFactory();
CheckOutHeader requestHeader = objFactory.createCheckOutHeader();
requestHeader.setMERCHANTID(MERCHANT_ID);
Date timestamp = new Date();
String encryptedPassword = getEncryptedPassword(MERCHANT_ID, PASSKEY, timestamp);
requestHeader.setPASSWORD(encryptedPassword);
requestHeader.setTIMESTAMP(String.valueOf(timestamp.getTime()));
ProcessCheckOutRequest checkOutRequest = objFactory.createProcessCheckOutRequest();
checkOutRequest = processCheckOut(timestamp);
soap.processCheckOut(checkOutRequest, requestHeader);
ProcessCheckOutResponse checkOutResponse = new ProcessCheckOutResponse();
checkOutResponse.getRETURNCODE();
checkOutResponse.getDESCRIPTION();
checkOutResponse.getTRXID();
checkOutResponse.getENCPARAMS();
checkOutResponse.getCUSTMSG();
}
private static ProcessCheckOutRequest processCheckOut(Date date){
ProcessCheckOutRequest checkOutRequest = new ProcessCheckOutRequest();
checkOutRequest.setMERCHANTTRANSACTIONID("54635469064");
checkOutRequest.setREFERENCEID("TD346534GH");
checkOutRequest.setAMOUNT(3.45);
checkOutRequest.setMSISDN("0721826284");
checkOutRequest.setENCPARAMS("");
checkOutRequest.setCALLBACKURL(CALLBACK_URL);
checkOutRequest.setCALLBACKMETHOD(CALLBACK_METHOD);
checkOutRequest.setTIMESTAMP(String.valueOf(date.getTime()));
return checkOutRequest;
}
/**
* Convert the concatenated string to bytes
* Hash the bytes to get arbitary binary data
* Convert the binary data to string use base64
*
* @param merchantId
* @param passkey
* @param date
* @return
*/
private static String getEncryptedPassword(String merchantId, String passkey, Date date) {
String encodedPassword = null;
StringBuilder builder = new StringBuilder(merchantId)
.append(passkey)
.append(date.getTime());
try {
String sha256 = getSHA256Hash(builder.toString());
return new String(Base64.encodeBase64(sha256.getBytes("UTF-8")));;
} catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) {
ex.printStackTrace();
}
return encodedPassword;
}
private static LNMOPortType outBoundLNMOCheckout(String url) {
System.setProperty("com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump", "true");
LnmoCheckoutService lnmoCheckoutService = new LnmoCheckoutService();
LNMOPortType soap = lnmoCheckoutService.getLnmoCheckout();
((BindingProvider)soap).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
url + "lnmo_checkout_server.php");
return soap;
}
private static String getSHA256Hash(String input) throws NoSuchAlgorithmException {
MessageDigest mDigest = MessageDigest.getInstance("SHA-256");
byte[] result = mDigest.digest(input.getBytes());
StringBuffer sb = new StringBuffer();
for (int i = 0; i < result.length; i++) {
sb.append(Integer.toString((result[i] & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
}
}
样品肥皂请求
[HTTP request - https://safaricom.co.ke/mpesa_online/lnmo_checkout_server.php]---
Accept: text/xml, multipart/related
Content-Type: text/xml; charset=utf-8
SOAPAction: ""
User-Agent: JAX-WS RI 2.2.9-b130926.1035 svn-revision#5f6196f2b90e9460065a4c2f4e30e065b245e51e
<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Header>
<ns2:CheckOutHeader xmlns:ns2="tns:ns">
<MERCHANT_ID>F2678987M</MERCHANT_ID>
<PASSWORD>QQFSOITZB5OJMJTW073/SCWLN5WMDL6LO0FP6DJZ8TQ=</PASSWORD>
<TIMESTAMP>1479116220855</TIMESTAMP>
</ns2:CheckOutHeader>
</S:Header>
<S:Body>
<ns2:processCheckOutRequest xmlns:ns2="tns:ns">
<MERCHANT_TRANSACTION_ID>54635469064</MERCHANT_TRANSACTION_ID>
<REFERENCE_ID>TD346534GH</REFERENCE_ID>
<AMOUNT>3.45</AMOUNT>
<MSISDN>0721826284</MSISDN>
<ENC_PARAMS></ENC_PARAMS>
<CALL_BACK_URL>https://makara.co.ke:8443/odt/checkout</CALL_BACK_URL>
<CALL_BACK_METHOD>lnmo</CALL_BACK_METHOD>
<TIMESTAMP>1479116220855</TIMESTAMP>
</ns2:processCheckOutRequest>
</S:Body>
</S:Envelope>
您只需联系 Safaricom 即可获取 Demo 测试组织详细信息(商家 ID 和密码)
Client jar
Source code