微信支付的整体流程图
微信小游戏支付,服务端主要开发的两个接口。
- getBalance()
查询玩家的米大师的余额币的数量,客户端根据返回的米大师余额币判断是拉起支付,还是直接判定余额币充足,不用再次支付,可以直接请求服务端调用 pay接口,扣完玩家的米大师余额币。 - pay()
用于扣除玩家的米大师余额币,服务端根据微信API返回的数据(错误码),判断是否扣除成功。
java主要实现的代码
- getBalance
/**
* @param userId 玩家在游戏内的唯一标识
* @param appid
* @param openid
* @param access_token 调用凭证
* @param pf 支付平台,android者windows
* @return
*/
public PayResult getBalance(Long userId, String appid, String openid, String access_token, String pf)
{
String unixTimestamp = Long.toString(System.currentTimeMillis()/1000L); //UNIX 时间戳,单位是秒
String signTemp = "appid=" + appid + "&offer_id=" + offer_id + "&openid="+ openid
+ "&pf=" + pf + "&ts=" + unixTimestamp + "&zone_id=" + zone_id
+ "&org_loc=" + getBalance_url_org_loc + "&method=POST"
+ "&secret=" + midas_secret;
String sig = HMACUtils.HMAC_SHA256(midas_secret, signTemp); //拿到签名
JSONObject postBody = new JSONObject(7);
postBody.put("openid", openid);
postBody.put("appid", appid);
postBody.put("offer_id", offer_id);
postBody.put("ts", unixTimestamp);
postBody.put("zone_id", zone_id);
postBody.put("pf", pf);
postBody.put("sig", sig);
String bodyJson = postBody.toJSONString();
StringEntity stringEntity = new StringEntity(bodyJson, "UTF-8");
stringEntity.setContentEncoding("UTF-8");
LoggerUtils.log(this.getClass(), userId, "payModel", "getBalance", "before", bodyJson, pf);
String getBalance_url = "https://api.weixin.qq.com/" + getBalance_url_org_loc + "?access_token=" + access_token;
HttpPost httpPost = new HttpPost(getBalance_url);
httpPost.setEntity(stringEntity);
httpPost.addHeader("Content-Type", "application/json");
httpPost.addHeader("Accept", "application/json");
CloseableHttpResponse response = null;
try
{
response = HttpUtils.getHttpClient().execute(httpPost);
String jsonStr = EntityUtils.toString(response.getEntity(), "utf-8");
LoggerUtils.log(this.getClass(), userId, "payModel", "getBalance", "after", jsonStr, pf);
JSONObject json = JSONObject.parseObject(jsonStr);
return PayResult.createPayResult(json.get("errcode"), json.get("errmsg"), json.get("balance"));
}
catch (Exception e)
{
LoggerUtils.error(this.getClass(), "getBalance", e, userId, pf);
return PayResult.exception_payResult;
}
finally
{
HttpClientUtils.closeQuietly(response);
}
}
- pay:客户端已经支付完成,服务端请求API扣除该账户的米大师余额币
/**
* @param userId 玩家的唯一标识
* @param appid
* @param openid
* @param access_token 接口凭证
* @param amt 要扣除的数量
* @param pf 平台
* @param createPayId 用于生成唯一订单号(可以借助redis的自增实现,保证全局唯一)
* @return
*/
public PayResult pay(Long userId, String appid, String openid, String access_token, Integer amt, String pf, Long createPayId)
{
String bill_no = DateFormatUtils.format(System.currentTimeMillis(), "yyyyMMddHHmmss_") + createPayId; //订单号,全局唯一
String unixTimestamp = Long.toString(System.currentTimeMillis()/1000L); //UNIX 时间戳,单位是秒
String signTemp = "amt=" +amt + "&appid=" + appid + "&bill_no=" + bill_no + "&offer_id=" + offer_id
+ "&openid="+ openid + "&pf=" + pf + "&ts=" + unixTimestamp + "&zone_id=" + zone_id
+ "&org_loc=" + pay_url_org_loc + "&method=POST&secret=" + midas_secret;
String sig = HMACUtils.HMAC_SHA256(midas_secret, signTemp); //拿到签名
JSONObject postBody = new JSONObject(9);
postBody.put("openid", openid);
postBody.put("appid", appid);
postBody.put("offer_id", offer_id);
postBody.put("ts", unixTimestamp);
postBody.put("zone_id", zone_id);
postBody.put("pf", pf);
postBody.put("amt", amt);
postBody.put("bill_no", bill_no);
postBody.put("sig", sig);
String bodyJson = postBody.toJSONString();
StringEntity stringEntity = new StringEntity(bodyJson, "UTF-8");
stringEntity.setContentEncoding("UTF-8");
LoggerUtils.log(this.getClass(), userId, "payModel", "pay", "before", amt, bill_no, bodyJson, pf);
String pay_url = "https://api.weixin.qq.com/" + pay_url_org_loc + "?access_token=" + access_token;
HttpPost httpPost = new HttpPost(pay_url);
httpPost.setEntity(stringEntity);
httpPost.addHeader("Content-Type", "application/json");
httpPost.addHeader("Accept", "application/json");
CloseableHttpResponse response = null;
try
{
response = HttpUtils.getHttpClient().execute(httpPost);
String jsonStr = EntityUtils.toString(response.getEntity(), "utf-8");
JSONObject json = JSONObject.parseObject(jsonStr);
return PayResult.createPayResult(json.get("errcode"), json.get("errmsg"), json.get("balance"), json.get("bill_no"));
}
catch (IOException e)
{
LoggerUtils.error(this.getClass(), "pay", e, amt, bill_no, pf);
return PayResult.exception_payResult;
}
finally
{
HttpClientUtils.closeQuietly(response);
}
}
其余重要的方法接口
- 发送http请求,使用http连接池,减少频繁创建性能开销。(保证线程安全)
public class HttpUtils
{
private static PoolingHttpClientConnectionManager poolConnManager = null;
private static CloseableHttpClient httpClient; //线程安全,支持所有的线程使用它发送http请求
static
{
try
{
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
SSLConnectionSocketFactory ssls = new SSLConnectionSocketFactory(builder.build());
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", ssls)
.build();
poolConnManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
poolConnManager.setMaxTotal(640);
poolConnManager.setDefaultMaxPerRoute(320);
httpClient = getConnection();
} catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
} catch (KeyStoreException e)
{
e.printStackTrace();
} catch (KeyManagementException e)
{
e.printStackTrace();
}
}
private static CloseableHttpClient getConnection()
{
RequestConfig config = RequestConfig
.custom()
.setConnectTimeout(5000)
.setSocketTimeout(5000)
.setConnectionRequestTimeout(5000)
.build();
CloseableHttpClient httpClient = HttpClients
.custom()
.setConnectionManager(poolConnManager) // 设置连接池管理
.setDefaultRequestConfig(config)
.setRetryHandler(new DefaultHttpRequestRetryHandler(3, false)) // 设置重试次数
.build();
return httpClient;
}
public static CloseableHttpClient getHttpClient()
{
return httpClient;
}
}
public class HMACUtils
{
public static String HMAC_SHA256(String key, String message)
{
String res = "";
try
{
Mac instance = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "HmacSHA256");
instance.init(secretKeySpec);
byte[] doFinal = instance.doFinal(message.getBytes());
res = byteArrayToHexString(doFinal);
}
catch (Exception e)
{
e.printStackTrace();
}
return res;
}
/**
* 将加密后的字节数组转换成字符串
*
* @param b 字节数组
* @return 字符串
*/
private static String byteArrayToHexString(byte[] b) {
StringBuilder hs = new StringBuilder();
String stmp;
for (int n = 0; b!=null && n < b.length; n++) {
stmp = Integer.toHexString(b[n] & 0XFF);
if (stmp.length() == 1)
hs.append('0');
hs.append(stmp);
}
return hs.toString().toUpperCase();
}
}
public class PayResult
{
private Boolean exception;
private Object errcode; //错误代码
private Object errmsg; //作物提示信息
private Object balance; //余额币的数目
private Object bill_no; //订单号
public final static WolfPayResult exception_payResult = new WolfPayResult(true);
public boolean isException()
{
if(null != exception && exception)
return true;
else
return false;
}
public static WolfPayResult createPayResult(Object errcode, Object errmsg, Object balance, Object bill_no)
{
WolfPayResult payResult = new WolfPayResult();
payResult.setErrcode(errcode);
payResult.setErrmsg(errmsg);
payResult.setBalance(balance);
payResult.setBill_no(bill_no);
return payResult;
}
public static WolfPayResult createPayResult(Object errcode, Object errmsg, Object balance)
{
WolfPayResult payResult = new WolfPayResult();
payResult.setErrcode(errcode);
payResult.setErrmsg(errmsg);
payResult.setBalance(balance);
return payResult;
}
public WolfPayResult()
{
}
public WolfPayResult(Boolean exception)
{
this.exception = exception;
}
get,set........
}