小程式實現微信支付java後端
阿新 • • 發佈:2019-02-11
第一步
進入小程式,下單,請求下單支付,呼叫小程式登入API來獲取Openid(https://mp.weixin.qq.com/debug/w ... .html#wxloginobject),生成商戶訂單,這些都是在小程式端完成的業務。
小程式端程式碼
第二步// pages/pay/pay.js var app = getApp(); Page({ data: {}, onLoad: function (options) { // 頁面初始化 options為頁面跳轉所帶來的引數 }, /* 微信支付 */ wxpay: function () { var that = this //登陸獲取code wx.login({ success: function (res) { console.log(res.code) //獲取openid that.getOpenId(res.code) } }); }, getOpenId: function (code) { var that = this; wx.request({ url: "https://api.weixin.qq.com/sns/jscode2session?appid=wxa142513e524e496c&secret=5d6a7d86048884e7c60f84f7aa85253c&js_code=" + code + "&grant_type=authorization_code", data: {}, method: 'GET', success: function (res) { console.log('返回openId') console.log(res.data) that.generateOrder(res.data.openid) }, fail: function () { // fail }, complete: function () { // complete } }) }, /**生成商戶訂單 */ generateOrder: function (openid) { var that = this //統一支付 wx.request({ url: 'http://localhost:8070/RMS/pay_pay.action', method: 'GET', data: { total_fee: '5', body: '支付測試', attach:'真假酒水' }, success: function (res) { console.log(res) var pay = res.data //發起支付 var timeStamp = pay[0].timeStamp; console.log("timeStamp:"+timeStamp) var packages = pay[0].package; console.log("package:"+packages) var paySign = pay[0].paySign; console.log("paySign:"+paySign) var nonceStr = pay[0].nonceStr; console.log("nonceStr:"+nonceStr) var param = { "timeStamp": timeStamp, "package": packages, "paySign": paySign, "signType": "MD5", "nonceStr": nonceStr }; that.pay(param) }, }) }, /* 支付 */ pay: function (param) { console.log("支付") console.log(param) wx.requestPayment({ timeStamp: param.timeStamp, nonceStr: param.nonceStr, package: param.package, signType: param.signType, paySign: param.paySign, success: function (res) { // success console.log("支付") console.log(res) wx.navigateBack({ delta: 1, // 回退前 delta(預設為1) 頁面 success: function (res) { wx.showToast({ title: '支付成功', icon: 'success', duration: 2000 }) }, fail: function () { // fail }, complete: function () { // complete } }) }, fail: function (res) { // fail console.log("支付失敗") console.log(res) }, complete: function () { // complete console.log("pay complete") } }) } })
呼叫支付統一下單API來獲取prepay_id,並將小程式調起支付資料需要簽名的欄位appId,timeStamp,nonceStr,package再次簽名(https://pay.weixin.qq.com/wiki/d ... ter=7_7&index=3)
後臺程式碼
後臺業務邏輯涉及到的工具類及引數封裝類package cn.it.shop.action; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import cn.it.shop.util.MessageUtil; import cn.it.shop.util.PayUtil; import cn.it.shop.util.PaymentPo; import cn.it.shop.util.UUIDHexGenerator; import net.sf.json.JSONArray; import net.sf.json.JSONObject; /** * @author * @version 建立時間:2017年1月21日 下午4:59:03 * 小程式端請求的後臺action,返回簽名後的資料傳到前臺 */ public class PayAction { private String total_fee;//總金額 private String body;//商品描述 private String detail;//商品詳情 private String attach;//附加資料 private String time_start;//交易起始時間 private String time_expire;//交易結束時間 private String openid;//使用者標識 public String pay() throws UnsupportedEncodingException, DocumentException{ private JSONArray jsonArray=new JSONArray(); body = new String(body.getBytes("UTF-8"),"ISO-8859-1"); String appid = "替換為自己的小程式ID";//小程式ID String mch_id = "替換為自己的商戶號";//商戶號 String nonce_str = UUIDHexGenerator.generate();//隨機字串 String today = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); String code = PayUtil.createCode(8); String out_trade_no = mch_id+today+code;//商戶訂單號 String spbill_create_ip = "替換為自己的終端IP";//終端IP String notify_url = "http://www.weixin.qq.com/wxpay/pay.php";//通知地址 String trade_type = "JSAPI";//交易型別 String openid="替換為使用者的openid";//使用者標識 /**/ PaymentPo paymentPo = new PaymentPo(); paymentPo.setAppid(appid); paymentPo.setMch_id(mch_id); paymentPo.setNonce_str(nonce_str); String newbody=new String(body.getBytes("ISO-8859-1"),"UTF-8");//以utf-8編碼放入paymentPo,微信支付要求字元編碼統一採用UTF-8字元編碼 paymentPo.setBody(newbody); paymentPo.setOut_trade_no(out_trade_no); paymentPo.setTotal_fee(total_fee); paymentPo.setSpbill_create_ip(spbill_create_ip); paymentPo.setNotify_url(notify_url); paymentPo.setTrade_type(trade_type); paymentPo.setOpenid(openid); // 把請求引數打包成陣列 Map sParaTemp = new HashMap(); sParaTemp.put("appid", paymentPo.getAppid()); sParaTemp.put("mch_id", paymentPo.getMch_id()); sParaTemp.put("nonce_str", paymentPo.getNonce_str()); sParaTemp.put("body", paymentPo.getBody()); sParaTemp.put("out_trade_no", paymentPo.getOut_trade_no()); sParaTemp.put("total_fee",paymentPo.getTotal_fee()); sParaTemp.put("spbill_create_ip", paymentPo.getSpbill_create_ip()); sParaTemp.put("notify_url",paymentPo.getNotify_url()); sParaTemp.put("trade_type", paymentPo.getTrade_type()); sParaTemp.put("openid", paymentPo.getOpenid()); // 除去陣列中的空值和簽名引數 Map sPara = PayUtil.paraFilter(sParaTemp); String prestr = PayUtil.createLinkString(sPara); // 把陣列所有元素,按照“引數=引數值”的模式用“&”字元拼接成字串 String key = "&key=替換為商戶支付金鑰"; // 商戶支付金鑰 //MD5運算生成簽名 String mysign = PayUtil.sign(prestr, key, "utf-8").toUpperCase(); paymentPo.setSign(mysign); //打包要傳送的xml String respXml = MessageUtil.messageToXML(paymentPo); // 列印respXml發現,得到的xml中有“__”不對,應該替換成“_” respXml = respXml.replace("__", "_"); String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";//統一下單API介面連結 String param = respXml; //String result = SendRequestForUrl.sendRequest(url, param);//發起請求 String result =PayUtil.httpRequest(url, "POST", param); // 將解析結果儲存在HashMap中 Map map = new HashMap(); InputStream in=new ByteArrayInputStream(result.getBytes()); // 讀取輸入流 SAXReader reader = new SAXReader(); Document document = reader.read(in); // 得到xml根元素 Element root = document.getRootElement(); // 得到根元素的所有子節點 @SuppressWarnings("unchecked") List elementList = root.elements(); for (Element element : elementList) { map.put(element.getName(), element.getText()); } // 返回資訊 String return_code = map.get("return_code");//返回狀態碼 String return_msg = map.get("return_msg");//返回資訊 System.out.println("return_msg"+return_msg); JSONObject JsonObject=new JSONObject() ; if(return_code=="SUCCESS"||return_code.equals(return_code)){ // 業務結果 String prepay_id = map.get("prepay_id");//返回的預付單資訊 String nonceStr=UUIDHexGenerator.generate(); JsonObject.put("nonceStr", nonceStr); JsonObject.put("package", "prepay_id="+prepay_id); Long timeStamp= System.currentTimeMillis()/1000; JsonObject.put("timeStamp", timeStamp+""); String stringSignTemp = "appId="+appid+"&nonceStr=" + nonceStr + "&package=prepay_id=" + prepay_id+ "&signType=MD5&timeStamp=" + timeStamp; //再次簽名 String paySign=PayUtil.sign(stringSignTemp, "&key=替換為自己的金鑰", "utf-8").toUpperCase(); JsonObject.put("paySign", paySign); jsonArray.add(JsonObject); } return "pay"; } public String getTotal_fee() { return total_fee; } public void setTotal_fee(String total_fee) { this.total_fee = total_fee; } public String getBody() { return body; } public void setBody(String body) { this.body = body; } public JSONArray getJsonArray() { return jsonArray; } public void setJsonArray(JSONArray jsonArray) { this.jsonArray = jsonArray; } public String getDetail() { return detail; } public void setDetail(String detail) { this.detail = detail; } public String getAttach() { return attach; } public void setAttach(String attach) { this.attach = attach; } public String getTime_start() { return time_start; } public void setTime_start(String time_start) { this.time_start = time_start; } public String getTime_expire() { return time_expire; } public void setTime_expire(String time_expire) { this.time_expire = time_expire; } public String getOpenid() { return openid; } public void setOpenid(String openid) { this.openid = openid; } }
MessageUtil
PaymentPo//封裝支付引數實體package cn.it.shop.util; import java.io.IOException; import java.io.Writer; import java.util.HashMap; import java.util.List; import javax.servlet.http.HttpServletRequest; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; 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; public class MessageUtil { public static HashMap parseXML(HttpServletRequest request) throws Exception, IOException{ HashMap map=new HashMap(); // 通過IO獲得Document SAXReader reader = new SAXReader(); Document doc = reader.read(request.getInputStream()); //得到xml的根節點 Element root=doc.getRootElement(); recursiveParseXML(root,map); return map; } private static void recursiveParseXML(Element root,HashMap map){ //得到根節點的子節點列表 List elementList=root.elements(); //判斷有沒有子元素列表 if(elementList.size()==0){ map.put(root.getName(), root.getTextTrim()); } else{ //遍歷 for(Element e:elementList){ recursiveParseXML(e,map); } } } private static XStream xstream = new XStream(new XppDriver() { public HierarchicalStreamWriter createWriter(Writer out) { return new PrettyPrintWriter(out) { // 對所有xml節點都增加CDATA標記 boolean cdata = true; public void startNode(String name, Class clazz) { super.startNode(name, clazz); } protected void writeText(QuickWriter writer, String text) { if (cdata) { writer.write(" writer.write(text); writer.write("]]>"); } else { writer.write(text); } } }; } }); public static String messageToXML(PaymentPo paymentPo){ xstream.alias("xml",PaymentPo.class); return xstream.toXML(paymentPo); } }
package cn.it.shop.util;
/**
* @author
* @version 建立時間:2017年1月21日 下午4:20:52
* 類說明
*/
public class PaymentPo {
private String appid;//小程式ID
private String mch_id;//商戶號
private String device_info;//裝置號
private String nonce_str;//隨機字串
private String sign;//簽名
private String body;//商品描述
private String detail;//商品詳情
private String attach;//附加資料
private String out_trade_no;//商戶訂單號
private String fee_type;//貨幣型別
private String spbill_create_ip;//終端IP
private String time_start;//交易起始時間
private String time_expire;//交易結束時間
private String goods_tag;//商品標記
private String total_fee;//總金額
private String notify_url;//通知地址
private String trade_type;//交易型別
private String limit_pay;//指定支付方式
private String openid;//使用者標識
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public String getMch_id() {
return mch_id;
}
public void setMch_id(String mch_id) {
this.mch_id = mch_id;
}
public String getNonce_str() {
return nonce_str;
}
public void setNonce_str(String nonce_str) {
this.nonce_str = nonce_str;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getOut_trade_no() {
return out_trade_no;
}
public void setOut_trade_no(String out_trade_no) {
this.out_trade_no = out_trade_no;
}
public String getTotal_fee() {
return total_fee;
}
public void setTotal_fee(String total_fee) {
this.total_fee = total_fee;
}
public String getNotify_url() {
return notify_url;
}
public void setNotify_url(String notify_url) {
this.notify_url = notify_url;
}
public String getTrade_type() {
return trade_type;
}
public void setTrade_type(String trade_type) {
this.trade_type = trade_type;
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public String getSpbill_create_ip() {
return spbill_create_ip;
}
public void setSpbill_create_ip(String spbill_create_ip) {
this.spbill_create_ip = spbill_create_ip;
}
public String getDevice_info() {
return device_info;
}
public void setDevice_info(String device_info) {
this.device_info = device_info;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
public String getAttach() {
return attach;
}
public void setAttach(String attach) {
this.attach = attach;
}
public String getFee_type() {
return fee_type;
}
public void setFee_type(String fee_type) {
this.fee_type = fee_type;
}
public String getTime_start() {
return time_start;
}
public void setTime_start(String time_start) {
this.time_start = time_start;
}
public String getTime_expire() {
return time_expire;
}
public void setTime_expire(String time_expire) {
this.time_expire = time_expire;
}
public String getGoods_tag() {
return goods_tag;
}
public void setGoods_tag(String goods_tag) {
this.goods_tag = goods_tag;
}
public String getLimit_pay() {
return limit_pay;
}
public void setLimit_pay(String limit_pay) {
this.limit_pay = limit_pay;
}
}
PayUtil package cn.it.shop.util;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.codec.digest.DigestUtils;
/**
* @author
* @version 建立時間:2017年1月17日 下午7:46:29 類說明
*/
public class PayUtil {
/**
* 簽名字串
* @param text需要簽名的字串
* @param key 金鑰
* @param input_charset編碼格式
* @return 簽名結果
*/
public static String sign(String text, String key, String input_charset) {
text = text + key;
return DigestUtils.md5Hex(getContentBytes(text, input_charset));
}
/**
* 簽名字串
* @param text需要簽名的字串
* @param sign 簽名結果
* @param key金鑰
* @param input_charset 編碼格式
* @return 簽名結果
*/
public static boolean verify(String text, String sign, String key, String input_charset) {
text = text + key;
String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset));
if (mysign.equals(sign)) {
return true;
} else {
return false;
}
}
/**
* @param content
* @param charset
* @return
* @throws SignatureException
* @throws UnsupportedEncodingException
*/
public static byte[] getContentBytes(String content, String charset) {
if (charset == null || "".equals(charset)) {
return content.getBytes();
}
try {
return content.getBytes(charset);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("MD5簽名過程中出現錯誤,指定的編碼集不對,您目前指定的編碼集是:" + charset);
}
}
/**
* 生成6位或10位隨機數 param codeLength(多少位)
* @return
*/
public static String createCode(int codeLength) {
String code = "";
for (int i = 0; i < codeLength; i++) {
code += (int) (Math.random() * 9);
}
return code;
}
private static boolean isValidChar(char ch) {
if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
return true;
if ((ch >= 0x4e00 && ch <= 0x7fff) || (ch >= 0x8000 && ch <= 0x952f))
return true;// 簡體中文漢字編碼
return false;
}
/**
* 除去陣列中的空值和簽名引數
* @param sArray 簽名引數組
* @return 去掉空值與簽名引數後的新簽名引數組
*/
public static Map paraFilter(Map sArray) {
Map result = new HashMap();
if (sArray == null || sArray.size() <= 0) {
return result;
}
for (String key : sArray.keySet()) {
String value = sArray.get(key);
if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
|| key.equalsIgnoreCase("sign_type")) {
continue;
}
result.put(key, value);
}
return result;
}
/**
* 把陣列所有元素排序,並按照“引數=引數值”的模式用“&”字元拼接成字串
* @param params 需要排序並參與字元拼接的引數組
* @return 拼接後字串
*/
public static String createLinkString(Map params) {
List keys = new ArrayList(params.keySet());
Collections.sort(keys);
String prestr = "";
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = params.get(key);
if (i == keys.size() - 1) {// 拼接時,不包括最後一個&字元
prestr = prestr + key + "=" + value;
} else {
prestr = prestr + key + "=" + value + "&";
}
}
return prestr;
}
/**
*
* @param requestUrl請求地址
* @param requestMethod請求方法
* @param outputStr引數
*/
public static String httpRequest(String requestUrl,String requestMethod,String outputStr){
// 建立SSLContext
StringBuffer buffer=null;
try{
URL url = new URL(requestUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod(requestMethod);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.connect();
//往伺服器端寫內容
if(null !=outputStr){
OutputStream os=conn.getOutputStream();
os.write(outputStr.getBytes("utf-8"));
os.close();
}
// 讀取伺服器端返回的內容
InputStream is = conn.getInputStream();
InputStreamReader isr = new InputStreamReader(is, "utf-8");
BufferedReader br = new BufferedReader(isr);
buffer = new StringBuffer();
String line = null;
while ((line = br.readLine()) != null) {
buffer.append(line);
}
}catch(Exception e){
e.printStackTrace();
}
return buffer.toString();
}
public static String urlEncodeUTF8(String source){
String result=source;
try {
result=java.net.URLEncoder.encode(source, "UTF-8");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
}
UUIDHexGenerator package cn.it.shop.util;
import java.net.InetAddress;
/**
* @author
* @version 建立時間:2017年1月17日 下午7:45:06 類說明
*/
public class UUIDHexGenerator {
private static String sep = "";
private static final int IP;
private static short counter = (short) 0;
private static final int JVM = (int) (System.currentTimeMillis() >>>;
private static UUIDHexGenerator uuidgen = new UUIDHexGenerator();
static {
int ipadd;
try {
ipadd = toInt(InetAddress.getLocalHost().getAddress());
} catch (Exception e) {
ipadd = 0;
}
IP = ipadd;
}
public static UUIDHexGenerator getInstance() {
return uuidgen;
}
public static int toInt(byte[] bytes) {
int result = 0;
for (int i = 0; i < 4; i++) {
result = (result << - Byte.MIN_VALUE + (int) bytes;
}
return result;
}
protected static String format(int intval) {
String formatted = Integer.toHexString(intval);
StringBuffer buf = new StringBuffer("00000000");
buf.replace(8 - formatted.length(), 8, formatted);
return buf.toString();
}
protected static String format(short shortval) {
String formatted = Integer.toHexString(shortval);
StringBuffer buf = new StringBuffer("0000");
buf.replace(4 - formatted.length(), 4, formatted);
return buf.toString();
}
protected static int getJVM() {
return JVM;
}
protected synchronized static short getCount() {
if (counter < 0) {
counter = 0;
}
return counter++;
}
protected static int getIP() {
return IP;
}
protected static short getHiTime() {
return (short) (System.currentTimeMillis() >>> 32);
}
protected static int getLoTime() {
return (int) System.currentTimeMillis();
}
public static String generate() {
return new StringBuffer(36).append(format(getIP())).append(sep).append(format(getJVM())).append(sep)
.append(format(getHiTime())).append(sep).append(format(getLoTime())).append(sep)
.append(format(getCount())).toString();
}
/**
* @param args
*/
public static void main(String[] args) {
String id="";
UUIDHexGenerator uuid = UUIDHexGenerator.getInstance();
/*
for (int i = 0; i < 100; i++) {
id = uuid.generate();
}*/
id = uuid.generate();
System.out.println(id);
}
}