最近寫了一個微信小程序的後端,微信支付(Java版)。在這裏分享一下開發過程吧。javascript
首先咱們要先去了解微信小程序支付開發的過程。
前端
這裏說一下流程。java
- 微信小程序端獲取到code,由微信小程序前端傳code到後端,
- 後端接收到code去調用微信官方接口換取openid。
- 生成商戶訂單,即經過xml格式包含文檔中必填的參數去調用統一下單接口(附下圖),返回prepay_id.
- 固然微信小程序須要二次簽名,一樣經過必填參數封裝,返回給微信小程序端。(其中有不少坑,下文慢慢講解)
正文開始web
一、微信小程序前端獲取到code算法
//獲取code的方法 wx.login({ success: res => { console.log("向後臺傳遞獲取用戶的信息的地址"); console.log(res.code); // 發送 res.code 到後臺換取 openId //此處能夠請求後臺接口獲取openid } })
2.後臺接收到code後,經過拼接字符串請求地址,返回openid(在後續附上工具類)spring
public static String getOpenId(String code) { String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + 你的APP_ID + "&secret=" + 你的APP_SECRET + "&js_code=" + code + "&grant_type=authorization_code"; HttpUtil httpUtil = new HttpUtil(); try { HttpResult httpResult = httpUtil.doGet(url, null, null); if (httpResult.getStatusCode() == 200) { JsonParser jsonParser = new JsonParser(); JsonObject obj = (JsonObject) jsonParser.parse(httpResult.getBody()); System.out.println("getOpenId: " + obj.toString()); if (obj.get("errcode") != null) { System.out.println("getOpenId returns errcode: " + obj.get("errcode")); return ""; } else { return obj.get("openid").toString(); } //return httpResult.getBody(); } } catch (Exception e) { e.printStackTrace(); } return ""; }
3.其次是調用統一下單接口,(這一步也是不少人採坑的地方)官方詳細文檔數據庫
- 統一下單接口訪問的接口爲:https://api.mch.weixin.qq.com/pay/unifiedorder
- 統一下單接口必填參數爲:[共十個]appid(小程序ID),mch_id(商戶號),nonce_str(隨機字符串),sign(簽名),body(商品描述),out_trade_no(商戶訂單號),total_fee(標價金額),spbill_create_ip(終端ip),notify_url(回調地址),trade_type(交易類型)
**注意:**這裏的nonce_str必須與再次簽名的隨機字符串爲同一值。apache
我這裏是經過封裝一個實體類,對類進行賦值取值。 (固然,嫌麻煩的能夠直接所有寫在配置類中)json
public class PayInfo implements Serializable { private String appid;//appid private String body;//商品描述 private String mch_id;//商戶號 //private String device_info; //設備號,小程序傳"WEB" private String nonce_str;//隨機字符串 private String notify_url;//通知地址 private String openid;//用戶標識 private String out_trade_no;//商戶訂單號 private String sign;//簽名 private String sign_type; //簽名類型 private String spbill_create_ip;//終端ip //private String detail;//商品詳情 //private String attach;附加數據 private String time_expire;//交易結束時間 private String time_start;//起始交易時間 private int total_fee;//商戶金額 private String trade_type; //交易類型,JSAPI //private String limit_pay; //指定支付方式,no_credit private String TimeStamp; private String repay_id;//這裏應該設置package,可是是關鍵字。 private String key;
…省略get和set…小程序
這一步是 對實體類設置須要的值。(酌情修改參數,沒必要填的能夠註釋掉,上面寫了必填參數!) 注意:微信官方要求md5加密必須符合字母表順序,這一步在寫實體類的時候要注意(屬性名要按字母表寫)。
set和get方法不影響。
private PayInfo createPayInfo(String openId, String clientIP, String randomNonceStr) { Date date = new Date(); String timeStart = TimeUtils.getFormatTime(date, Constant.TIME_FORMAT); String timeExpire = TimeUtils.getFormatTime(TimeUtils.addDay(date, Constant.TIME_EXPIRE), Constant.TIME_FORMAT); String randomOrderId = CommonUtil.getRandomOrderId(); PayInfo payInfo = new PayInfo(); payInfo.setAppid(Constant.APP_ID); payInfo.setMch_id(Constant.MCH_ID); payInfo.setDevice_info("WEB"); payInfo.setNonce_str(randomNonceStr); payInfo.setSign_type("MD5"); //默認即爲MD5 payInfo.setBody("JSAPI支付測試");//這裏若是是中文須要進行轉碼,否則可能會報錯 payInfo.setAttach("支付測試4luluteam"); payInfo.setOut_trade_no(randomOrderId); payInfo.setTotal_fee(1); payInfo.setSpbill_create_ip(clientIP); payInfo.setTime_start(timeStart); payInfo.setTime_expire(timeExpire); payInfo.setNotify_url(Constant.URL_NOTIFY); payInfo.setTrade_type("JSAPI"); payInfo.setLimit_pay("no_credit"); payInfo.setOpenid(openId); return payInfo;
設置好參數,這一步是對字符串進行拼接。目的是md5加密!(酌情刪減)
private String getSign(PayInfo payInfo) throws Exception { StringBuffer sb = new StringBuffer(); sb.append("appid=" + payInfo.getAppid()) .append("&attach=" + payInfo.getAttach()) .append("&body=" + payInfo.getBody()) .append("&device_info=" + payInfo.getDevice_info()) .append("&limit_pay=" + payInfo.getLimit_pay()) .append("&mch_id=" + payInfo.getMch_id()) .append("&nonce_str=" + payInfo.getNonce_str()) .append("¬ify_url=" + payInfo.getNotify_url()) .append("&openid=" + payInfo.getOpenid()) .append("&out_trade_no=" + payInfo.getOut_trade_no()) .append("&sign_type=" + payInfo.getSign_type()) .append("&spbill_create_ip=" + payInfo.getSpbill_create_ip()) .append("&time_expire=" + payInfo.getTime_expire()) .append("&time_start=" + payInfo.getTime_start()) .append("&total_fee=" + payInfo.getTotal_fee()) .append("&trade_type=" + payInfo.getTrade_type()) .append("&key=" + Constant.APP_KEY); log.error("排序後的拼接參數:" + sb.toString()); return CommonUtil.getMD5(sb.toString().trim()).toUpperCase(); }
這裏進行統一下單。返回的是prepay_id這個值。 若是這裏報了簽名錯誤等問題。[解決方案]一、仔細檢查10個必填參數是否按順序拼接。二、檢查一下appid,mch_id等(或者更改一下key[微信端會抽風])
/** * 調用統一下單接口 * @param openId */ private String unifiedOrder(String openId, String clientIP, String randomNonceStr) { try { String url = Constant.URL_UNIFIED_ORDER; PayInfo payInfo = createPayInfo(openId, clientIP, randomNonceStr); //這裏對payInfo對象的九個必須參數進行加密,獲得sign值。 String md5 = getSign(payInfo); payInfo.setSign(md5); log.error("md5 value: " + md5); String xml = CommonUtil.payInfoToXML(payInfo); xml = xml.replace("__", "_").replace("<![CDATA[1]]>", "1"); //xml = xml.replace("__", "_").replace("<![CDATA[", "").replace("]]>", ""); log.error(xml); StringBuffer buffer = HttpUtil.httpsRequest(url, "POST", xml); log.error("unifiedOrder request return body: \n" + buffer.toString()); Map<String, String> result = CommonUtil.parseXml(buffer.toString()); String return_code = result.get("return_code"); if(StringUtils.isNotBlank(return_code) && return_code.equals("SUCCESS")) { String return_msg = result.get("return_msg"); if(StringUtils.isNotBlank(return_msg) && !return_msg.equals("OK")) { //log.error("統一下單錯誤!"); return ""; } String prepay_Id = result.get("prepay_id"); return prepay_Id; } else { return ""; } } catch (Exception e) { e.printStackTrace(); } return ""; }
輸出結果:
得不到prepay_id,極可能是sign出錯了。建議多核對幾遍。
咱們獲得prepay_id以後,將組合數據再次進行簽名。
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.example.company.service.GoodsOrderService; import com.example.company.utils.HttpClientUtils; import com.example.company.utils.Pay.*; import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import org.apache.commons.lang3.StringUtils; import org.jdom.JDOMException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.DigestUtils; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; public R prePay(String code, HttpServletRequest request) throws IllegalAccessException { //調用靜態方法getOpenId String openId = getOpenId(code);//獲取到openid openId = openId.replace("\"", "").trim(); //這裏獲取到prepayId String prepayId = unifiedOrder(openId, clientIP, randomNonceStr); //獲取客戶端ip String clientIP = CommonUtil.getClientIp(request); //System.out.println("prepayId: " + prepayId); //二次簽名加密封裝 final long time = System.currentTimeMillis() / 1000; String timeStamp = String.valueOf(time); String paySign = "appId=" + Constant.APP_ID + "&nonceStr=" + randomNonceStr + "&package=prepay_id=" + prepayId + "&signType=MD5" + "&timeStamp=" + timeStamp + "&key="+Constant.APP_KEY; String sign = DigestUtils.md5DigestAsHex(paySign.getBytes(StandardCharsets.UTF_8)).toUpperCase(); //System.out.println("sign===="+sign); //再次簽名 Map map = new HashMap(); //map.put("appId", Constant.APP_ID);//獲取appid map.put("nonceStr", randomNonceStr);//返回隨機字符串 map.put("package", "prepay_id=" + prepayId);//返回prepayId map.put("paySign", sign); map.put("signType", "MD5"); map.put("timeStamp", timeStamp);//獲取時間戳 String content = null; try { content = JSON.toJSONString(map);//以json字符串形式返還給調用者 System.out.println("content" + content); } catch (Exception e) { e.printStackTrace(); } return R.ok().data("content", content); }
獲得的"content"的字符串值爲:
作到這裏,已經幾近成功了。還差最後一步,回調函數(當支付成功時,在數據庫中修改"已支付"成功字段。)
注意:一、回調地址必須是公網上的地址。(項目部署到服務器上纔可進行訪問)。 二、回調地址中不能添加任何參數。
public String notifyUrl(HttpServletRequest request, HttpServletResponse response) throws IOException, JDOMException { //System.out.println(request); 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(); /** for(Map.Entry<String,String> entry:params.entrySet()){ System.out.println(entry.getKey()+" ======[[]]====="+entry.getValue()); } */ /** * 這裏獲得的params的字符串對。 * transaction_id ======[[]]=====4200000819202011192847521125 * nonce_str ======[[]]=====BBJIJ1vrDqxistqhFukLIjuHe7PP21DP * bank_type ======[[]]=====OTHERS * openid ======[[]]=====ozP-C4m-93TNpk0F40yvzJA12wa0 * sign ======[[]]=====F90FFB4B9DEA5F7846B28E93209C3A93 * fee_type ======[[]]=====CNY * mch_id ======[[]]=====顯示本身的商戶號 * cash_fee ======[[]]=====1 * out_trade_no ======[[]]=====20201119165722672 * appid ======[[]]=====顯示本身的appid * total_fee ======[[]]=====1 * trade_type ======[[]]=====JSAPI * result_code ======[[]]=====SUCCESS * time_end ======[[]]=====20201119165807 * is_subscribe ======[[]]=====N * return_code ======[[]]=====SUCCESS */ 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 out_trade_no = String.valueOf(Long.parseLong(params.get("out_trade_no").split("O")[0])); System.out.println("out_trade_no==="+out_trade_no); Date accountTime = DateUtil.stringtoDate(params.get("time_end"), "yyyyMMddHHmmss"); System.out.println("accountTime==="+accountTime); String ordertime = DateUtil.dateToString(new Date(), "yyyy-MM-dd HH:mm:ss"); System.out.println("ordertime==="+ordertime); String totalAmount = String.valueOf(v); System.out.println("totalAmount==="+totalAmount); String appId = params.get("appid"); System.out.println("appId==="+appId); String tradeNo = params.get("transaction_id"); System.out.println("tradeNo==="+tradeNo); return_data.put("return_code", "SUCCESS"); return_data.put("return_msg", "OK"); return StringUtil.GetMapToXML(return_data); } }
附上工具類。
public class Constant { //public static final String DOMAIN = "https://i-test.com.cn"; public static final String APP_ID = "你的id";//小程序ID public static final String APP_SECRET = "你的secret"; public static final String APP_KEY = "商戶key"; public static final String MCH_ID = "你的商戶號"; //商戶號 public static final String URL_UNIFIED_ORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder"; public static final String URL_CHECK_ORDER = "https://api.mch.weixin.qq.com/pay/orderquery"; public static final String URL_NOTIFY = "你的公網地址";//回調 地址 public static final String TIME_FORMAT = "yyyyMMddHHmmss"; public static final int TIME_EXPIRE = 2; //單位是day } public class PayCommonUtil { /** * 驗證回調簽名 * @return */ public static boolean isTenpaySign(Map<String, String> map) { String characterEncoding="utf-8"; String charset = "utf-8"; String signFromAPIResponse = map.get("sign"); if (signFromAPIResponse == null || signFromAPIResponse.equals("")) { System.out.println("API返回的數據簽名數據不存在,有可能被第三方篡改!!!"); return false; } System.out.println("服務器回包裏面的簽名是:" + signFromAPIResponse); //過濾空 設置 TreeMap SortedMap<String,String> packageParams = new TreeMap(); for (String parameter : map.keySet()) { String parameterValue = map.get(parameter); String v = ""; if (null != parameterValue) { v = parameterValue.trim(); } packageParams.put(parameter, v); } StringBuffer sb = new StringBuffer(); Set es = packageParams.entrySet(); Iterator it = es.iterator(); while(it.hasNext()) { Map.Entry entry = (Map.Entry)it.next(); String k = (String)entry.getKey(); String v = (String)entry.getValue(); if(!"sign".equals(k) && null != v && !"".equals(v)) { sb.append(k + "=" + v + "&"); } } sb.append("key=" + API_KEY); //將API返回的數據根據用簽名算法進行計算新的簽名,用來跟API返回的簽名進行比較 //算出簽名 String resultSign = ""; String tobesign = sb.toString(); if (null == charset || "".equals(charset)) { resultSign = MD5Util.MD5Encode(tobesign, characterEncoding).toUpperCase(); }else{ try{ resultSign = MD5Util.MD5Encode(tobesign, characterEncoding).toUpperCase(); }catch (Exception e) { resultSign = MD5Util.MD5Encode(tobesign, characterEncoding).toUpperCase(); } } String tenpaySign = ((String)packageParams.get("sign")).toUpperCase(); return tenpaySign.equals(resultSign); } } import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.core.util.QuickWriter; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.io.xml.PrettyPrintWriter; import com.thoughtworks.xstream.io.xml.XppDriver; import org.apache.commons.lang3.StringUtils; import org.dom4j.Document; import org.dom4j.DocumentHelper; import org.dom4j.Element; import javax.servlet.http.HttpServletRequest; import java.io.Writer; import java.math.BigInteger; import java.security.MessageDigest; import java.util.*; public class CommonUtil { public static String getRandomOrderId() { // UUID.randomUUID().toString().replace("-","") Random random = new Random(System.currentTimeMillis()); int value = random.nextInt(); while (value < 0) { value = random.nextInt(); } return value + ""; } private static XStream xstream = new XStream(new XppDriver() { public HierarchicalStreamWriter createWriter(Writer out) { return new PrettyPrintWriter(out) { //增長CDATA標記 boolean cdata = true; @SuppressWarnings("rawtypes") public void startNode(String name, Class clazz) { super.startNode(name, clazz); } protected void writeText(QuickWriter writer, String text) { if (cdata) { writer.write("<![CDATA["); writer.write(text); writer.write("]]>"); } else { writer.write(text); } } }; } }); public static String payInfoToXML(PayInfo pi) { xstream.alias("xml", pi.getClass()); return xstream.toXML(pi); } @SuppressWarnings("unchecked") public static Map<String, String> parseXml(String xml) throws Exception { Map<String, String> map = new HashMap<>(); Document document = DocumentHelper.parseText(xml); Element root = document.getRootElement(); List<Element> elementList = root.elements(); for (Element e : elementList) map.put(e.getName(), e.getText()); return map; } public static String getClientIp(HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){ //屢次反向代理後會有多個ip值,第一個ip纔是真實ip int index = ip.indexOf(","); if(index != -1){ return ip.substring(0,index); }else{ return ip; } } ip = request.getHeader("X-Real-IP"); if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){ return ip; } return request.getRemoteAddr(); } /** * 對字符串md5加密 * * @param str * @return */ public static String getMD5(String str) throws Exception { try { // 生成一個MD5加密計算摘要 MessageDigest md = MessageDigest.getInstance("MD5"); // 計算md5函數 md.update(str.getBytes()); // digest()最後肯定返回md5 hash值,返回值爲8爲字符串。由於md5 hash值是16位的hex值,實際上就是8位的字符 // BigInteger函數則將8位的字符串轉換成16位hex值,用字符串來表示;獲得字符串形式的hash值 return new BigInteger(1, md.digest()).toString(16); } catch (Exception e) { throw new Exception("MD5加密出現錯誤"); } } } import org.apache.http.Consts; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.SSLContextBuilder; import org.apache.http.conn.ssl.TrustSelfSignedStrategy; import org.apache.http.impl.client.BasicCookieStore; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import javax.net.ssl.HttpsURLConnection; import java.io.*; import java.net.URL; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; import java.util.Map; public class HttpUtil { // User-Agent public static final String USERAGENT_FIREFOX = "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:39.0) Gecko/20100101 Firefox/39.0"; public static final String USERAGENT_IE = "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko"; private CloseableHttpClient httpClient; private BasicCookieStore cookieStore; private HttpGet get; private HttpPost post; public static StringBuffer httpsRequest(String requestUrl, String requestMethod, String output) throws IOException { URL url = new URL(requestUrl); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setDoInput(true); connection.setUseCaches(false); connection.setRequestMethod(requestMethod); if (null != output) { OutputStream outputStream = connection.getOutputStream(); outputStream.write(output.getBytes("UTF-8")); outputStream.close(); } // 從輸入流讀取返回內容 InputStream inputStream = connection.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; StringBuffer buffer = new StringBuffer(); while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); inputStream.close(); inputStream = null; connection.disconnect(); return buffer; } public HttpResult doGet(String url, Map<String, String> headers, Map<String, String> params) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException, ClientProtocolException, IOException { if (url == null|| url.equals("")) { return null; } SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build()); cookieStore = new BasicCookieStore(); CloseableHttpClient httpclient = HttpClients.custom().setDefaultCookieStore(cookieStore) .setSSLSocketFactory(sslsf).build(); HttpResult result = null; try { url = url + "?" + parseParams(params); HttpGet httpget = new HttpGet(url); httpget.setHeaders(parseHeader(headers)); CloseableHttpResponse response = httpclient.execute(httpget); try { HttpEntity entity = response.getEntity(); if (entity != null) { result = new HttpResult(); result.setCookies(cookieStore.getCookies()); result.setStatusCode(response.getStatusLine().getStatusCode()); result.setHeaders(response.getAllHeaders()); result.setBody(EntityUtils.toString(entity)); } } finally { response.close(); } } finally { httpclient.close(); } return result; } public HttpResult doPost(String url, Map<String, String> headers, Map<String, String> postData, String encoding) throws Exception { if (url == null|| url.equals("")) { return null; } if (encoding == null|| encoding.equals("")) { encoding = "utf-8"; } SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build()); cookieStore = new BasicCookieStore(); CloseableHttpClient httpClient = HttpClients.custom().setDefaultCookieStore(cookieStore) .setSSLSocketFactory(sslsf).build(); post = new HttpPost(url); List<NameValuePair> list = new ArrayList<NameValuePair>(); for (String tmp : postData.keySet()) { list.add(new BasicNameValuePair(tmp, postData.get(tmp))); } post.setEntity(new UrlEncodedFormEntity(list, encoding)); post.setHeaders(parseHeader(headers)); CloseableHttpResponse response = httpClient.execute(post); HttpEntity entity = response.getEntity(); HttpResult result = new HttpResult(); result.setCookies(cookieStore.getCookies()); result.setStatusCode(response.getStatusLine().getStatusCode()); result.setHeaders(response.getAllHeaders()); result.setBody(EntityUtils.toString(entity, encoding)); close(entity, response); return result; } }
在這裏作個小總結吧。
微信官方的文檔意思就是。
第一次簽名:appid,mch_id,nonce_str,body,out_trade_no,total_fee,spbill_create_ip,notify_url,trade_type
這九個參數進行md5加密,爲sign,而後九個參數+sign以xml的格式發送請求給微信統一下單API返回的xml格式中有數據。
第二次簽名:appId,nonceStr,randomNonceStr,package,signType,timeStamp,key[七個參數]進行拼接簽名。
String paySign = 「appId=」 + Constant.APP_ID + 「&nonceStr=」 +
randomNonceStr + 「&package=prepay_id=」 + prepayId + 「&signType=MD5」 +
「&timeStamp=」 + timeStamp + 「&key=」+Constant.APP_KEY;
而後paySign拼接以後進行md5加密。
最後返回
("nonceStr", randomNonceStr);//返回隨機字符串<br> ("package", "prepay_id=" + prepayId);//返回prepayId<br> ("paySign", sign);<br> ("signType", "MD5");<br> ("timeStamp", timeStamp);//獲取時間戳<br>
結束
好了。我這裏是只作了微信小程序的支付及回調。