應用寶-ysdk-米大師 對接道具直購伺服器端下單模式 java服務端開發日誌
阿新 • • 發佈:2018-12-17
用於接收回調請求的linux伺服器證書配置:
回撥伺服器配置分三種,前兩種是當服務部署在騰訊雲上時的配置方式,第三種為服務部署在自己伺服器上是的配置方式: hosting應用on CVM(即應用部署在騰訊CVM伺服器上): -發貨URL只需HTTP協議即可,不需要使用SSL安全協議。 -必須使用9001埠(內網埠,需開發者主動啟用,用apache iis或nginx做一個web監聽,埠改成9001)。 hosting應用on CEE_V2(即應用部署在騰訊CEE_V2伺服器上): -發貨URL只需HTTP協議即可,不需要使用SSL安全協議。 -必須使用9001埠(內網埠,需開發者主動啟用,用apache iis或nginx做一個web監聽,埠改成9001)。 -路徑必須以ceecloudpay開頭,即支付相關程式碼必須都放到應用根目錄下的“ceecloudpay”目錄下。 -對於CEE其發貨URL的IP只能填寫為10.142.11.27或者10.142.52.17(詳見:CEE_V2訪問雲支付)。 non-hosting應用(即應用部署在開發者自己的伺服器上) -發貨URL必須使用HTTPS協議。 -必須使用443埠(外網埠)。 這裡採用non-hosting應用配置方式,方式為在linux伺服器的nginx/conf/nginx.conf檔案下新增如下配置(nginx版本1.14.0):
# HTTPS server server { listen 443 ssl; server_name localhost; #"/usr/.../"為自己放證書檔案的目錄地址 ssl_certificate /usr/.../1**_**6.crt; ssl_certificate_key /usr/.../1**_**6.key; ssl_client_certificate /usr/.../ca.crt; ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; }
配置完後重新載入nginx使配置生效。
伺服器下單:
伺服器請求下單引數:
HashMap<String, String> params = new HashMap<>();//params型別不能是Map<String,String>會報錯,需要改為HsahMap<String,String> //必填引數 params.put("openid", "");//app登陸獲取 params.put("openkey", "");//app登陸獲取 params.put("appid", APP_ID);//應用id params.put("ts", String.valueOf(System.currentTimeMillis() / 1000L));//時間戳(單位:秒) params.put("payitem", product_id + "*" + price * 10 + "*1");//使用x*p*num的格式,x表示物品ID,p表示單價(以Q點為單位,1Q幣=10Q點,單價的制定需遵循騰訊定價規範),num表示預設的購買數量 params.put("goodsmeta", "充值*金幣充值");//物品資訊,格式必須是name*des,批量購買套餐時也只能有1個道具名稱和1個描述,即給出該套餐的名稱和描述 params.put("goodsurl", "http://.../coin.png");//物品的圖片url(長度<512字元) params.put("pf", "");//app登陸獲取 params.put("pfkey", "");//app登陸獲取 params.put("zoneid", "1");//應用如果沒有分割槽:傳zoneid=1 //可選引數 params.put("amt", "1");//道具總價值。應用寶支援的qq支付和微信支付方式會有折扣(qq點和qq卡一般沒有折扣),導致回撥的amt數值與請求下單時的amt值不一致,所以回撥避免用amt做金額驗證,如有必要,可以在回撥程式碼裡採用Double.parseDouble(payitem.split("\\*")[1])的值做金額驗證 params.put("appmode", "1");//(可選)1表示使用者不可以修改物品數量,2 表示使用者可以選擇購買物品的數量 params.put("format", "json");//json、jsonp_$func。預設json。如果jsonp,字首為:$func 例如:format=jsonp_sample_pay,返回格式字首為:sample_pay() params.put("userip", "");//使用者ip params.put("app_metadata", "");//發貨時透傳給應用。長度必須<=128字元,可以傳商戶id,便於回撥後續做驗證
生成簽名:
//SnsSigCheck為騰訊開放平臺提供的簽名方法:[SDK下載](http://qzonestyle.gtimg.cn/qzone/vas/opensns/res/doc/Java_SDK_V3.0.6.zip)
params.put("sig", SnsSigCheck.makeSig("POST", "/v3/r/mpay/buy_goods_m", params,APP_KEY + "&"));//java呼叫簽名方法必須用post請求,APP_KEY為app金鑰
伺服器下單請求cookie:
//不必自己生成cookie直接呼叫官方工具包的方法,所以直接傳入HashMap<String,String>作為cookie的配置即可
HashMap<String, String> cookies = new HashMap<>();
if ("qq".equalsIgnoreCase(txOpenPayOrderPost.getLoginType())) {//這裡的wx/qq需前端傳入
cookies.put("session_id", "openid");
cookies.put("session_type", "kp_actoken");
cookies.put("org_loc", SnsSigCheck.encodeUrl("/v3/r/mpay/buy_goods_m"));//org_loc的值需用官方工具類呼叫SnsSigCheck.encodeUrl方法做urlEncode,java自帶的URLEncoder.encode方法因為不符合騰訊的規範,不建議使用
} else if ("wx".equalsIgnoreCase(txOpenPayOrderPost.getLoginType())) {
cookies.put("session_id", "hy_gameid");
cookies.put("session_type", "wc_actoken");
cookies.put("org_loc", SnsSigCheck.encodeUrl("/v3/r/mpay/buy_goods_m"));
}
伺服器下單請求方法:
//https://ysdktest.qq.com/mpay/buy_goods_m為測試伺服器
String orderInfo = SnsNetwork.postRequest("https://ysdktest.qq.com/mpay/buy_goods_m", params, cookies,"http");
請求正確的返回值為:
{
"ret": 0,
"token": "7***2",
"url_params": "/**/***/**/mobile_goods_info?token_id=7**2",
"attach": ""
}
請求錯誤的返回值為:
{
"ret": **,//不為0的值
"msg": ***
}
最好做一個判斷:
JSONObject jsonObject = JSON.parseObject(orderInfo);
if (jsonObject.getIntValue("ret") == 0) {
logger.error("下單成功");
} else {
logger.error("下單失敗");
}
支付回撥:
//支付回撥的介面必須部署在前面配置了證書的linux伺服器上,並在應用寶配置介面做配置
@RequestMapping(value = "/notify")
@ResponseBody
public String verify(HttpServletRequest request, HttpServletResponse response) throws Exception {
HashMap<String, String> params = (HashMap<String, String>) RequestParamsUtil.parseParams(request);
logger.error(params.toString());
String appid = params.get("appid");//應用的唯一ID。可以通過appid查詢APP基本資訊。
String appmeta = params.get("appmeta");//透傳引數 可以用來獲取商戶訂單號
String billno = params.get("billno");//支付流水號(64個字元長度。該欄位和openid合起來是唯一的)。
String token = params.get("token");//應用呼叫v3/pay/buy_goods介面成功返回的交易token
String payitem = params.get("payitem");//接收標準格式為ID*price*num G001*10*1
String sig = params.get("sig");//伺服器簽名
Long orderId = Long.parseLong(appmeta.split("\\*")[0]);//從appmeta中擷取的透傳引數商戶支付流水號
Double amount = Double.parseDouble(payitem.split("\\*")[1]);
boolean signVerified = SnsSigCheck.verifySig(request.getMethod(), "/v3/r/mpay/buy_goods_m", params,
APP_KEY + "&", sig);//此處嘗試呼叫官方工具類驗證失敗
if(signVerified){
logger.error("驗證成功");
//業務程式碼,自定義資訊驗證及資料庫資訊回寫等
}else{
logger.error("驗證失敗");
return JSONObject.toJSONString(mkResultMap(-2, "簽名錯誤"));//這裡的ret值會影響之後騰訊給前端回撥的錯誤碼值,計算方式為500000-ret.例如此處ret=-2,回撥錯誤碼即為499998,ret=2,回撥錯誤碼為500002,利用這一點可以寫出分類比較詳細的錯誤驗證
}
}
/**
* 生成成功回覆結果map
*
* @param ret 結果code
* @param msg 結果msg
*/
private Map<String, Object> mkResultMap(int ret, String msg) {
Map<String, Object> map = new HashMap<>();
map.put("ret", ret);
map.put("msg", msg);
return map;
}
其中RequestParamsUtil工具類程式碼:
/**
* 根據請求資料獲取引數值map
*
* @param request HttpServletRequest請求物件
* @return HttpServletRequest內封裝的請求引數構建的map(key(引數名):value(引數值))
*/
public static Map<String, String> parseParams(HttpServletRequest request) {
Map<String, String[]> requestParams = request.getParameterMap();
Map<String, String> params = new HashMap<>();
for (String name : requestParams.keySet()) {
String[] values = requestParams.get(name);
StringBuilder valueStr = new StringBuilder();
for (int i = 0; i < values.length; i++) {
valueStr.append(
(i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",");
}
params.put(name, valueStr.toString());
}
return params;
}