微信app支付(android端+java後臺)
00:00 / 07:03正常
本實例項目運行條件:
開發環境: 【Android Studio】
-
到微信開放平臺註冊帳號並且創建移動應用
https://open.weixin.qq.com/cgi-bin/frame?t=home/app_tmpl&lang=zh_CN
Column 1 | Column 2 | Column 3 |
---|---|---|
Text | Text | Text |
-
獲得移動應用的權限【微信支付】
這個權限要求比較高,需要公司資質 並且 每年需要支付300元 才能開通 (這裏不作講解, 具體到官網上申請)
- 配置應用簽名, 這個簽名通過 android打包文件jks生成或者keystore生成 【如何生成jks文件】
簽名文件生成方法:
3.1 keytool -list -v -keystore jks文件(或者keystore文件)
3.2 獲取指紋證書md5值, 將md5中的冒號去掉, 大寫換成小寫 (詳情)
總結: 微信開放平臺Android應用簽名的本質便是我們簽名文件jks(或者keystore)的MD5值
- 配置支付密鑰, 【如何配置密鑰】 【微信配置密鑰官網】
- 應用程序開發完成後,debug模式是無法完成支付的,應用程序必須由相應的jks簽名之後生成的apk包安裝在手機上才能進行分支付(微信會校驗應用簽名)
- 支付流程講解
2.1 android程序啟動後如下第一張圖, 點擊【確認支付】
2.1.1 android端向後臺請求獲得預支付信息
2.1.2 後臺根據微信官網平臺上的 配置信息 加上 訂單信息 生成預支付信息
2.1.3 android端根據預支付信息 拉起微信支付頁面進行支付(見下面第二張圖)
- 代碼詳解(Android端)
3.1 在android studio中引入 微信需要使用的jar包
3.2 在android工程對應的包名下面新建 包以及類, wxapi/WXPayEntryActivity
在AndroidManifest.xml中引用 WXPayEntryActivityandroid:allowBackup="true"
br/>android:allowBackup="true"
android:icon="@drawable/desk"
br/>android:theme="@style/AppTheme">
android:screenOrientation="portrait"
android:label="@string/app_name">
activity
android:name=".activity.MainActivity"
android:screenOrientation="portrait"
android:label="@string/app_name">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".wxapi.WXPayEntryActivity"
android:screenOrientation="portrait"
android:exported="true"
android:launchMode="singleTop"/>
</application>
WXPayEntryActivity 代碼詳情(支付完成後,onResp會被調用,BaseResp.ErrCode.ERR_OK 表明支付成功)
public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler {
private static final String TAG = "WXPayEntryActivity";
private IWXAPI api;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Constant.wxApi.handleIntent(getIntent(), this);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
api.handleIntent(intent, this);
}
@Override
public void onReq(BaseReq req) {
}
/**
* 得到支付結果回調
*/
@Override
public void onResp(BaseResp resp)
{
Log.i(TAG, "onPayFinish, errCode = " + resp.errCode);
String strPayResult = "";
switch (resp.errCode) {
case BaseResp.ErrCode.ERR_OK:
Toast.makeText(this, "付款成功!", Toast.LENGTH_SHORT).show();
break;
case BaseResp.ErrCode.ERR_USER_CANCEL:
//分享取消
//Toast.makeText(this, "付款取消!", Toast.LENGTH_SHORT).show();
//Constant.WEIXIN_PAY_STATUS = "PAY_CANCEL";
break;
case BaseResp.ErrCode.ERR_AUTH_DENIED:
//分享拒絕
//Toast.makeText(this, "付款拒絕!", Toast.LENGTH_SHORT).show();
//Constant.WEIXIN_PAY_STATUS = "PAY_DENY";
break;
}
//向之前頁面返回支付結果信息
/*Intent intent = new Intent();
intent.putExtra("payResult", strPayResult);
setResult(100, intent);*/
finish();
}
}
3.3 支付按鈕的點擊事件
通過http協議向後臺請求相應的預支付信息, 根據這些信息組裝相應的信息來調用微信接口, 拉起微信支付界面
@OnClick(R.id.pay)
public void pay(){
String orderNum = OrderInfo.generateOutTradeNo();
payService
.wpay(orderNum, totalPrice, address.getText().toString() + "-外賣訂單")
.subscribe(new Action1<WeiXinPrePay>() {@Override
br/>@Override
if (Constant.wxApi != null) {
PayReq req = new PayReq();
req.appId = payInfo.getAppId();// 微信開放平臺審核通過的應用APPID
req.partnerId = payInfo.getMchId();// 微信支付分配的商戶號
req.prepayId = payInfo.getPrepayId();// 預支付訂單號,app服務器調用“統一下單”接口獲取
req.nonceStr = payInfo.getNonceStr();// 隨機字符串,不長於32位,服務器小哥會給咱生成
req.timeStamp = payInfo.getTimeStamp();// 時間戳,app服務器小哥給出
req.packageValue = "WXPay";// 固定值Sign=WXPay,可以直接寫死,服務器返回的也是這個固定值
req.sign = payInfo.getPaySign();// 簽名,服務器小哥給出,他會根據: om/wiki/doc/api/app/app.php?chapter=4_3指導得到這個
Constant.wxApi.sendReq(req);
}
}
}, new Action1<Throwable>() {@Override
br/>@Override
showToast(throwable.getMessage());
}
});
}
4 微信支付Java後臺
4.1 後臺代碼結構圖
4.2 微信配置信息 Config.properties
4.3 方法wxpay用於生成預支付訂單信息
方法notifyWeiXinPay用於微信支付成功後的回調, 註意: 在手機端使用微信支付成功後,微信服務器會根據提供的回調地址進行回調, parameterMap.put("notify_url", wxnotify); (見下面代碼)
在局域網是無法進行回調的,必須將你的服務端放在公網上進行測試, 回調函數會被多次調用,如果第一次成功後,你可以將業務數據狀態標誌為已處理, 對於相同訂單的其它回調就不需要再次處理了@Controller
br/>@Controller
public class PayController {
String timeMillis = String.valueOf(System.currentTimeMillis() / 1000);
String randomString = PayCommonUtil.getRandomString(32);
//支付成功後的回調函數
public static String wxnotify = "http://gepanjiang.hk1.tunnelfrp.cc/WxPay/pay/notifyWeiXinPay.htm";
public PayController() {
System.out.println("MainController構造函數");
}
/**
* @param totalAmount 支付金額
* @param description 描述
* @param request
* @return
*/
@RequestMapping(value = "/wxpay.htm", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Result wxpay(HttpServletRequest request) {
Result result = new Result();
Long userId = new Long(1);//baseController.getUserId();
BigDecimal totalAmount = new BigDecimal(request.getParameter("totalPrice"));
String trade_no = "";
String description="";
try {
trade_no = new String(request.getParameter("orderNum").getBytes("ISO-8859-1"),"UTF-8");
description = request.getParameter("description");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String openId = "";
Map<String, String> map = weixinPrePay(trade_no,totalAmount,description,openId,request);
SortedMap<String, Object> finalpackage = new TreeMap<String, Object>();
finalpackage.put("appId", ConfigManager.getInstance().getConfigItem("WXAppID")/*PayCommonUtil.APPID*/);
finalpackage.put("mchId", ConfigManager.getInstance().getConfigItem("MCH_ID"));
Long time = (System.currentTimeMillis() / 1000);
finalpackage.put("timeStamp", time.toString());
finalpackage.put("nonceStr", map.get("nonce_str"));
finalpackage.put("prepayId", map.get("prepay_id"));
finalpackage.put("package", "Sign=WXPay");
finalpackage.put("signType", "MD5");
String sign = PayCommonUtil.createSign("UTF-8", finalpackage);
finalpackage.put("paySign", sign);//官方文檔上是sign,當前示例代碼是paySign 可能以前的
WeiXinPrePay prePay = new WeiXinPrePay();
prePay.setAppId(ConfigManager.getInstance().getConfigItem("WXAppID"));
prePay.setMchId(ConfigManager.getInstance().getConfigItem("MCH_ID"));
prePay.setTimeStamp(time.toString());
prePay.setNonceStr(map.get("nonce_str"));
prePay.setPrepayId(map.get("prepay_id"));
prePay.setSignType("MD5");
prePay.setPaySign("paySign");
result.setData(prePay);
result.setStateCode(GeneralConstant.SUCCESS);
result.setDesc("微信支付加載成功");
return result;
}
/**
* 統一下單
* 應用場景:商戶系統先調用該接口在微信支付服務後臺生成預支付交易單,返回正確的預支付交易回話標識後再在APP裏面調起支付。
* @param trade_no
* @param totalAmount
* @param description
* @param openid
* @param sym
* @param request
* @return
*/
@SuppressWarnings("unchecked")
public Map<String, String> weixinPrePay(String trade_no,BigDecimal totalAmount,
String description, String openid, HttpServletRequest request) {
SortedMap<String, Object> parameterMap = new TreeMap<String, Object>();
parameterMap.put("appid", ConfigManager.getInstance().getConfigItem("WXAppID")); //應用appid
parameterMap.put("mch_id", ConfigManager.getInstance().getConfigItem("MCH_ID")/*PayCommonUtil.MCH_ID*/); //商戶號
//parameterMap.put("device_info", "WEB");
parameterMap.put("nonce_str", randomString);
parameterMap.put("body", description);
parameterMap.put("out_trade_no", trade_no);
parameterMap.put("fee_type", "CNY");
System.out.println("jiner");
BigDecimal total = totalAmount.multiply(new BigDecimal(100)); //接口中參數支付金額單位為【分】,參數值不能帶小數,所以乘以100
java.text.DecimalFormat df=new java.text.DecimalFormat("0");
parameterMap.put("total_fee", df.format(total));
System.out.println("jiner2");
parameterMap.put("spbill_create_ip", PayCommonUtil.getRemoteHost(request));
parameterMap.put("notify_url", wxnotify);
parameterMap.put("trade_type", "APP");//"JSAPI"
//trade_type為JSAPI是 openid為必填項
//parameterMap.put("openid", openid);
System.out.println("");
String sign = PayCommonUtil.createSign("UTF-8", parameterMap);
System.out.println("jiner2");
parameterMap.put("sign", sign);
String requestXML = PayCommonUtil.getRequestXml(parameterMap);
System.out.println(requestXML);
String result = PayCommonUtil.httpsRequest(
"https://api.mch.weixin.qq.com/pay/unifiedorder", "POST",
requestXML);
System.out.println(result);
Map<String, String> map = null;
try {
map = PayCommonUtil.doXMLParse(result);
} catch (JDOMException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return map;
}
/**
* 此函數會被執行多次,如果支付狀態已經修改為已支付,則下次再調的時候判斷是否已經支付,如果已經支付了,則什麽也執行
* @param request
* @param response
* @return
* @throws IOException
* @throws JDOMException
*/
@RequestMapping(value = "notifyWeiXinPay.htm", produces = MediaType.APPLICATION_JSON_VALUE)
// @RequestDescription("支付回調地址")@ResponseBody
br/>@ResponseBody
System.out.println("微信支付回調");
InputStream inStream = request.getInputStream();
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
String resultxml = new String(outSteam.toByteArray(), "utf-8");
Map<String, String> params = PayCommonUtil.doXMLParse(resultxml);
outSteam.close();
inStream.close();
Map<String,String> return_data = new HashMap<String,String>();
if (!PayCommonUtil.isTenpaySign(params)) {
// 支付失敗
return_data.put("return_code", "FAIL");
return_data.put("return_msg", "return_code不正確");
return StringUtil.GetMapToXML(return_data);
} else {
System.out.println("===============付款成功==============");
// ------------------------------
// 處理業務開始
// ------------------------------
// 此處處理訂單狀態,結合自己的訂單數據完成訂單狀態的更新
// ------------------------------
String total_fee = params.get("total_fee");
double v = Double.valueOf(total_fee) / 100;
String out_trade_no = String.valueOf(Long.parseLong(params.get("out_trade_no").split("O")[0]));
Date accountTime = DateUtil.stringtoDate(params.get("time_end"), "yyyyMMddHHmmss");
String ordertime = DateUtil.dateToString(new Date(), "yyyy-MM-dd HH:mm:ss");
String totalAmount = String.valueOf(v);
String appId = params.get("appid");
String tradeNo = params.get("transaction_id");
return_data.put("return_code", "SUCCESS");
return_data.put("return_msg", "OK");
return StringUtil.GetMapToXML(return_data);
}
}
}
微信app支付(android端+java後臺)