java開(kāi)發(fā)微信公眾號(hào)支付
最近做了微信公眾號(hào)支付的開(kāi)發(fā),由于是第一次做也摸索了幾天的時(shí)間,也只是達(dá)到了實(shí)現(xiàn)功能的水平,并沒(méi)有太多考慮到性能問(wèn)題,所以這篇文章比較適合初學(xué)者。
微信公眾號(hào)支付的總體其實(shí)很簡(jiǎn)單,大致就分為三步。第一步需要獲取用戶授權(quán);第二步調(diào)用統(tǒng)一下單接口獲取預(yù)支付id;第三步H5調(diào)起微信支付的內(nèi)置的js。下面介紹具體每一步的開(kāi)發(fā)流程。
一 首先要明確微信公眾號(hào)支付屬于網(wǎng)頁(yè)版支付,所以相較于app的直接調(diào)取微信支付要多一步微信授權(quán)。也就是需要獲取用戶的openid。微信公眾號(hào)使用的交易類型是JSAPI,所以統(tǒng)一下單接口的文檔明確的寫到

因此我們必須去獲取openid,同時(shí)也可以處理一些我們需要的邏輯。獲取用戶授權(quán)有兩種方式:1.scope=snsapi_base;2.scope=snsapi_userinfo.我使用的是snsapi_base
Scope為snsapi_base
Scope為snsapi_userinfo
微信的官方文檔也有對(duì)各個(gè)參數(shù)的詳細(xì)說(shuō)明,我就關(guān)鍵的參數(shù)仔細(xì)的說(shuō)明一下。首先appid就不多說(shuō)了就是你微信公眾號(hào)的appid固定寫死的,redirect_uri這個(gè)參數(shù)是最重要的,這個(gè)地址是訪問(wèn)你處理的接口地址。你可以在這個(gè)鏈接上拼接上你所需要的參數(shù),一般你是要把訂單的金額傳到這個(gè)接口里的,訪問(wèn)這個(gè)鏈接的時(shí)候微信會(huì)給你code你需要用它去獲取openid,記得要對(duì)其進(jìn)行urlencode處理。state參數(shù)可以理解為擴(kuò)展字段,其他的參數(shù)都是固定寫法就不在多做介紹了。下面是獲取openid的代碼片段。
//獲取openId
HttpClientUtil util = HttpClientUtil.getInstance();
Map<String, String> map = new HashMap<String, String>();
map.put("appid", WxPayConfig.APPID);
map.put("secret", WxPayConfig.APPSECRET);
map.put("code", code);
map.put("grant_type", WxPayConfig.GRANT_TYPE);
String returnStr = util.doPostRetString("https://api.weixin.qq.com/sns/oauth2/access_token", null,map);
logger.info("returnStr:[" + returnStr + "]");
AccessToken at = JSON.parseObject(returnStr, AccessToken.class);
AccessToken.java
public class AccessToken {
private String access_token;
private String expires_in;
private String refresh_token;
private String openid;
private String scope;
private String unionid;
public String getAccess_token() {
return access_token;
}
public void setAccess_token(String access_token) {
this.access_token = access_token;
}
public String getExpires_in() {
return expires_in;
}
public void setExpires_in(String expires_in) {
this.expires_in = expires_in;
}
public String getRefresh_token() {
return refresh_token;
}
public void setRefresh_token(String refresh_token) {
this.refresh_token = refresh_token;
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public String getUnionid() {
return unionid;
}
public void setUnionid(String unionid) {
this.unionid = unionid;
}
@Override
public String toString() {
return "AccessToken [access_token=" + access_token + ", expires_in="
+ expires_in + ", refresh_token=" + refresh_token + ", openid="
+ openid + ", scope=" + scope + ", unionid=" + unionid + "]";
}
}
二 我們獲取了openid后,就可以進(jìn)行下一步的統(tǒng)一下單的開(kāi)發(fā)了。微信上統(tǒng)一下單接口的文檔寫的比較詳細(xì)了,具體的參數(shù)含義我就不多做介紹了。下面直接貼最直觀的代碼,特別提醒的是一定要注意簽名的正確。簽名所使用的key并不是AppSecret而是你申請(qǐng)時(shí)自己定義的商戶key。
//統(tǒng)一下單
WxPaySendData data = new WxPaySendData();
data.setAppid(WxPayConfig.APPID);
data.setAttach("微信支付");
data.setBody("微信公眾號(hào)支付");
data.setMch_id(WxPayConfig.MCHID);
data.setNonce_str(nonceStr);
data.setNotify_url(WxPayConfig.NOTIFY_URL);
data.setOut_trade_no(tradeNo);
data.setTotal_fee((int)(fee*100));//單位:分
data.setTrade_type("JSAPI");
data.setSpbill_create_ip(ip);
data.setOpenid(at.getOpenid());
String returnXml = UnifiedorderService.unifiedOrder(data,WxPayConfig.KEY);
WxPayReturnData reData = new WxPayReturnData();
XStream xs1 = new XStream(new DomDriver());
xs1.alias("xml", WxPayReturnData.class);
reData = (WxPayReturnData) xs1.fromXML(returnXml);
UnifiedorderService.java
public class UnifiedorderService {
private final static Logger logger = LoggerFactory.getLogger(UnifiedorderService.class);
public static String unifiedOrder(WxPaySendData data,String key){
//統(tǒng)一下單支付
String returnXml = null;
try {
//生成sign簽名
SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();
parameters.put("appid", data.getAppid());
parameters.put("attach", data.getAttach());
parameters.put("body", data.getBody());
parameters.put("mch_id", data.getMch_id());
parameters.put("nonce_str", data.getNonce_str());
parameters.put("notify_url", data.getNotify_url());
parameters.put("out_trade_no", data.getOut_trade_no());
parameters.put("total_fee", data.getTotal_fee());
parameters.put("trade_type", data.getTrade_type());
parameters.put("spbill_create_ip", data.getSpbill_create_ip());
parameters.put("openid", data.getOpenid());
parameters.put("device_info", data.getDevice_info());
logger.info("SIGN:"+WxSign.createSign(parameters,key));
data.setSign(WxSign.createSign(parameters,key));
XStream xs = new XStream(new DomDriver("UTF-8",new XmlFriendlyNameCoder("-_", "_")));
xs.alias("xml", WxPaySendData.class);
String xml = xs.toXML(data);
logger.info("統(tǒng)一下單xml為:\n" + xml);
HttpClientUtil util = HttpClientUtil.getInstance();
returnXml = util.doPostForString("https://api.mch.weixin.qq.com/pay/unifiedorder", null, xml);
logger.info("返回結(jié)果:" + returnXml);
} catch (Exception e) {
e.printStackTrace();
}
return returnXml;
}
}
WxSign
public class WxSign {
private static String characterEncoding = "UTF-8";
@SuppressWarnings("rawtypes")
public static String createSign(SortedMap<Object,Object> parameters,String key){
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();//所有參與傳參的參數(shù)按照accsii排序(升序)
Iterator it = es.iterator();
while(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
String k = (String)entry.getKey();
Object v = entry.getValue();
if(null != v && !"".equals(v)
&& !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + key);
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
}
public static String getNonceStr() {
Random random = new Random();
return MD5Util.MD5Encode(String.valueOf(random.nextInt(10000)), "UTF-8");
}
public static String getTimeStamp() {
return String.valueOf(System.currentTimeMillis() / 1000);
}
}
最后要提一下的是NOTIFY_URL回調(diào)地址,接收微信支付異步通知回調(diào)地址。
三 通過(guò)上面的操作我們獲得了預(yù)支付交易會(huì)話標(biāo)識(shí)prepay_id,這樣我們就可以進(jìn)行最后一步的操作了。使用H5調(diào)起支付api。
//H5調(diào)起支付
attr.addAttribute("appId", reData.getAppid());
attr.addAttribute("timeStamp", WxSign.getTimeStamp());
attr.addAttribute("nonceStr", reData.getNonce_str());
attr.addAttribute("package", "prepay_id="+reData.getPrepay_id());
attr.addAttribute("signType", "MD5");
SortedMap<Object,Object> signMap = new TreeMap<Object,Object>();
signMap.put("appId", reData.getAppid());
signMap.put("timeStamp", WxSign.getTimeStamp());
signMap.put("nonceStr", reData.getNonce_str());
signMap.put("package", "prepay_id="+reData.getPrepay_id());
signMap.put("signType", "MD5");
logger.info("PaySIGN:"+WxSign.createSign(signMap,WxPayConfig.KEY));
attr.addAttribute("paySign", WxSign.createSign(signMap,WxPayConfig.KEY));
將需要的參數(shù)傳給頁(yè)面后,使用微信提供方法調(diào)起支付。
<script>
function getUrlParam(name) {
//構(gòu)造一個(gè)含有目標(biāo)參數(shù)的正則表達(dá)式對(duì)象
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
//匹配目標(biāo)參數(shù)
var r = window.location.search.substr(1).match(reg);
//返回參數(shù)值
if (r != null)
return unescape(r[2]);
return null;
}
function onBridgeReady() {
var appId = getUrlParam('appId');
var timeStamp = getUrlParam('timeStamp');
var nonceStr = getUrlParam('nonceStr');
var Package = getUrlParam('package');
var signType = getUrlParam('signType');
var paySign = getUrlParam('paySign');
WeixinJSBridge.invoke('getBrandWCPayRequest', {
"appId" : appId,//"wx2421b1c4370ec43b", //公眾號(hào)名稱,由商戶傳入
"timeStamp" : timeStamp,//"1395712654", //時(shí)間戳,自1970年以來(lái)的秒數(shù)
"nonceStr" : nonceStr,//"e61463f8efa94090b1f366cccfbbb444", //隨機(jī)串
"package" : Package,//"prepay_id=u802345jgfjsdfgsdg888",
"signType" : signType,//"MD5", //微信簽名方式:
"paySign" : paySign,//"70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信簽名
}, function(res) { // 使用以上方式判斷前端返回,微信團(tuán)隊(duì)鄭重提示:res.err_msg將在用戶支付成功后返回 ok,但并不保證它絕對(duì)可靠。
//alert(res.err_msg);
if (res.err_msg == "get_brand_wcpay_request:ok") {
alert("支付成功");
}
if (res.err_msg == "get_brand_wcpay_request:cancel") {
alert("交易取消");
}
if (res.err_msg == "get_brand_wcpay_request:fail") {
alert("支付失敗");
}
});
}
function callPay() {
if (typeof WeixinJSBridge == "undefined") {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', onBridgeReady,
false);
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
} else {
onBridgeReady();
}
}
</script>
在返回結(jié)果的地方可以自定義一些自己的返回頁(yè)面。
總結(jié):由于我也是第一次做,寫這篇文章是想記錄一下自己的工作成果,和分享給一下也是新手的朋友們可以有一些幫助,最后希望有好的見(jiàn)解朋友可以留言討論,大家一起學(xué)習(xí)進(jìn)步。
以上就是關(guān)于java開(kāi)發(fā)微信公眾支付的所有內(nèi)容了,希望大家能夠喜歡。
- java微信公眾號(hào)企業(yè)付款開(kāi)發(fā)
- Java版微信公眾號(hào)支付開(kāi)發(fā)全過(guò)程
- 微信公眾號(hào)開(kāi)發(fā)之設(shè)置自定義菜單實(shí)例代碼【java版】
- java微信公眾號(hào)支付開(kāi)發(fā)之現(xiàn)金紅包
- 微信公眾號(hào)開(kāi)發(fā)之回復(fù)圖文消息java代碼
- Java微信公眾號(hào)開(kāi)發(fā)之通過(guò)微信公眾號(hào)獲取用戶信息
- java微信公眾號(hào)開(kāi)發(fā)案例
- 用Java設(shè)計(jì)模式中的觀察者模式開(kāi)發(fā)微信公眾號(hào)的例子
- java微信公眾號(hào)開(kāi)發(fā)第一步 公眾號(hào)接入和access_token管理
- Java開(kāi)發(fā)微信公眾號(hào)接收和被動(dòng)回復(fù)普通消息
- java微信公眾號(hào)開(kāi)發(fā)(搭建本地測(cè)試環(huán)境)
- Java 微信公眾號(hào)開(kāi)發(fā)相關(guān)總結(jié)
相關(guān)文章
Java并發(fā)編程之ReentrantLock可重入鎖的實(shí)例代碼
這篇文章主要介紹了Java并發(fā)編程之ReentrantLock可重入鎖的實(shí)例代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02
java自定義日志輸出文件(log4j日志文件輸出多個(gè)自定義日志文件)
打印日志的在程序中是必不可少的,如果需要將不同的日志打印到不同的地方,則需要定義不同的Appender,然后定義每一個(gè)Appender的日志級(jí)別、打印形式和日志的輸出路徑,下面看一個(gè)示例吧2014-01-01
Spring Boot如何使用Spring Security進(jìn)行安全控制
要實(shí)現(xiàn)訪問(wèn)控制的方法多種多樣,可以通過(guò)Aop、攔截器實(shí)現(xiàn),也可以通過(guò)框架實(shí)現(xiàn),本文將具體介紹在Spring Boot中如何使用Spring Security進(jìn)行安全控制。2017-04-04
Java之idea @NotNull @Nullable 注解使用
這篇文章主要介紹了Java之idea @NotNull @Nullable 注解使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01
Spring?web開(kāi)發(fā)教程之Request獲取3種方式
這篇文章主要給大家介紹了關(guān)于Spring?web開(kāi)發(fā)教程之Request獲取3種方式的相關(guān)資料,request對(duì)象是從客戶端向服務(wù)器發(fā)出請(qǐng)求,包括用戶提交的信息以及客戶端的一些信息,需要的朋友可以參考下2023-11-11

