1. 程式人生 > >微信app支付(android端+java後臺)

微信app支付(android端+java後臺)

微信 APP 支付

本文講解使用微信支付接口完成在android開發的原生態app中完成微信支付功能, 文章具體講解了前端android如何集成微信支付功能以及後臺如何組裝前端需要支付信息, 話不多話, 具體看文章內容吧
00:00 / 07:03正常

本實例項目運行條件:

開發環境: 【Android Studio】

  1. 到微信開放平臺註冊帳號並且創建移動應用

    https://open.weixin.qq.com/cgi-bin/frame?t=home/app_tmpl&lang=zh_CN
    技術分享圖片

Column 1 Column 2 Column 3
Text Text Text
  1. 獲得移動應用的權限【微信支付】

    這個權限要求比較高,需要公司資質 並且 每年需要支付300元 才能開通 (這裏不作講解, 具體到官網上申請)

    技術分享圖片

    1. 配置應用簽名, 這個簽名通過 android打包文件jks生成或者keystore生成 【如何生成jks文件】

    簽名文件生成方法:

    3.1 keytool -list -v -keystore jks文件(或者keystore文件)

    3.2 獲取指紋證書md5值, 將md5中的冒號去掉, 大寫換成小寫 (詳情)

    總結: 微信開放平臺Android應用簽名的本質便是我們簽名文件jks(或者keystore)的MD5值
    技術分享圖片

    1. 配置支付密鑰, 【如何配置密鑰】 【微信配置密鑰官網】
      技術分享圖片
      1. 應用程序開發完成後,debug模式是無法完成支付的,應用程序必須由相應的jks簽名之後生成的apk包安裝在手機上才能進行分支付(微信會校驗應用簽名)
  2. 支付流程講解

2.1 android程序啟動後如下第一張圖, 點擊【確認支付】

2.1.1 android端向後臺請求獲得預支付信息

2.1.2 後臺根據微信官網平臺上的 配置信息 加上 訂單信息 生成預支付信息

2.1.3 android端根據預支付信息 拉起微信支付頁面進行支付(見下面第二張圖)
技術分享圖片技術分享圖片

  1. 代碼詳解(Android端)

3.1 在android studio中引入 微信需要使用的jar包
技術分享圖片
3.2 在android工程對應的包名下面新建 包以及類, wxapi/WXPayEntryActivity
技術分享圖片
在AndroidManifest.xml中引用 WXPayEntryActivityandroid:allowBackup="true"

android:icon="@drawable/desk"
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後臺)