17.支付寶支付微信支付
簡(jiǎn)單介紹一下支付寶支付開發(fā)流程
我們先要在支付寶官網(wǎng)申請(qǐng)公司企業(yè)賬號(hào)并開通一個(gè)應(yīng)用,在應(yīng)用里簽約APP支付功能,然后獲取到這幾個(gè)值①商戶appid ②商戶公鑰、私鑰 ③支付寶公鑰 ④支付寶網(wǎng)關(guān)地址
其中商戶appid,商戶公鑰校赤、私鑰,都可以得到,支付寶公鑰需要下載一個(gè)支付寶公鑰生成器,
獲取到支付寶公鑰,然后將商戶公鑰上傳到支付寶上,支付寶就可以解鎖我們傳過去帶商戶私鑰的信息.將支付寶的SDK集成到項(xiàng)目系統(tǒng)里,下載java的SDK,解壓后得alipay-sdk-java20180122110032.jar叔锐、commons-logging-1.1.1.jar,其中alipay-sdk-java20180122110032.jar需要安裝到本地maven倉庫,將這兩個(gè)jar添加到pom.xml里,環(huán)境就搭建好了.
-
我們需要寫3個(gè)對(duì)外提供的接口即可
1.用戶點(diǎn)擊“立即購買”時(shí)調(diào)用商戶后臺(tái)接口涕烧,后臺(tái)返回加簽后的訂單信息字符串2.在支付完成之后袁辈,支付寶異步通知商戶后臺(tái)訂單的付款情況
3.在支付完成之后区赵,跳轉(zhuǎn)回APP時(shí)惭缰,APP調(diào)用商戶后臺(tái)進(jìn)行最終付款校驗(yàn)
實(shí)現(xiàn)支付寶接口詳細(xì)過程
1.去支付寶官網(wǎng)申請(qǐng)公司企業(yè)賬號(hào)并開通一個(gè)應(yīng)用,在應(yīng)用里簽約APP支付功能
具體的申請(qǐng)截圖步驟笼才,在這里我就不詳細(xì)說了漱受,因?yàn)檫@不是文章的重點(diǎn),可參考支付寶官網(wǎng)骡送。
經(jīng)過這一步昂羡,我們可以得過開發(fā)中需要用到的幾個(gè)參數(shù)
①商戶appid ②商戶公鑰、私鑰 ③支付寶公鑰 ④支付寶網(wǎng)關(guān)地址
解釋一下這幾個(gè)參數(shù):
1.商戶appid是識(shí)別商戶的唯一ID摔踱,是讓支付寶識(shí)別虐先,我們到底是哪一個(gè)商戶,這樣支付寶就能識(shí)別商戶對(duì)應(yīng)的賬號(hào)派敷、用戶號(hào)赴穗、收款賬號(hào)...等等一系列信息。
2.商戶公鑰膀息、私鑰以及支付寶公鑰這3個(gè)參數(shù)是對(duì)商戶系統(tǒng)與支付寶進(jìn)行信息交互的數(shù)字簽名用的般眉,相信各位大學(xué)里也有學(xué)過關(guān)于數(shù)字簽名的一些知識(shí),在這里潜支,我就簡(jiǎn)單說一下我理解的過程:首先是商戶系統(tǒng)需要給支付寶發(fā)送信息(支付甸赃、查詢等等....),涉及錢方面冗酿,咱們當(dāng)前要謹(jǐn)慎一點(diǎn)對(duì)吧埠对,所以我們需要對(duì)發(fā)送之前的信息加把鎖(用商戶私鑰進(jìn)行簽名),然后再發(fā)送給支付寶裁替。支付寶收到商戶發(fā)送的信息之后项玛,發(fā)現(xiàn)上了把鎖,那肯定得要一把鑰匙(商戶公鑰)來解鎖對(duì)吧弱判,所以商戶在跟支付寶簽約APP支付功能的時(shí)候襟沮,就得把這把鑰匙上傳給支付寶了,支付寶就可以用商戶的公鑰進(jìn)行解鎖了昌腰。反過來也是一樣开伏,支付寶需要發(fā)送信息給商戶信息,先用支付寶的私鑰進(jìn)行簽名遭商,再發(fā)送給商戶系統(tǒng)固灵,商戶系統(tǒng)收到支付寶反饋過來的信息后,再用支付寶的公鑰進(jìn)行解密劫流。在這里我們并沒有用到支付寶的私鑰巫玻,所以我們并不需要得到支付寶的私鑰。這里放一個(gè)生成私鑰公鑰的支付寶官方工具
3.支付寶網(wǎng)關(guān)地址祠汇,是用來配置發(fā)送給支付寶的網(wǎng)關(guān)地址的仍秤。
2.將支付寶的SDK集成到項(xiàng)目系統(tǒng)里
支付寶的SDK指的就是支付寶提供的工具Jar包給我們開發(fā)者,SDK封裝了大量的基礎(chǔ)功能座哩,使我們可以快速開發(fā)支付寶接口徒扶。這也是我在前面說的比微信支付接口更容易實(shí)現(xiàn)的原因。獲取支付寶SDK地址:支付寶SDK下載地址根穷,這里我選擇JAVA的SDK姜骡。下載解壓后的SDK得到:
alipay-sdk-java20180122110032.jar、commons-logging-1.1.1.jar是我們需要導(dǎo)入到項(xiàng)目里的屿良,因?yàn)轫?xiàng)目后臺(tái)的大致的架構(gòu)是maven+springBoot+jpa,所以我們需要從maven里導(dǎo)入jar包圈澈,首先是導(dǎo)入commons-logging-1.1.1.jar,不用多說尘惧,咱直接在pom.xml里加上:
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
然后是alipay-sdk-java20180122110032.jar康栈,如果你也像上面一樣直接加入到pom.xml,會(huì)發(fā)現(xiàn),咦啥么,怎么一直下載不下來登舞。當(dāng)然alipy的包在線上的maven倉庫并沒有,所以我們需要導(dǎo)入到本地的maven倉庫悬荣。前提是配置好maven的環(huán)境變量菠秒,將包放在G:\alipay\sdk下,然后打開dos窗口,cd進(jìn)入到G:\alipay\sdk下氯迂,執(zhí)行maven如下命令:
mvn install:install-file -DgroupId=com.alipay -DartifactId=sdk-java -Dversion=20180122110032 -Dpackaging=jar -Dfile=alipay-sdk-java20180122110032.jar
導(dǎo)入成功后践叠,在項(xiàng)目的pom.xml里繼續(xù)添加
<dependency>
<groupId>com.alipay</groupId>
<artifactId>sdk-java</artifactId>
<version>20180122110032</version>
</dependency>
到此,我們就順利把支付寶SDK集成到項(xiàng)目里嚼蚀,是不是有點(diǎn)小興奮禁灼,我們很快可以開發(fā)了!
3.看支付寶提供的API和網(wǎng)上各位牛人總結(jié)的經(jīng)驗(yàn)轿曙,后臺(tái)使用支付寶的SDK與支付寶進(jìn)行交互
先看一下支付寶支付流程:
首先弄捕,我們來理一理開發(fā)的思路,按照我當(dāng)前項(xiàng)目的需求拳芙,關(guān)于支付這一塊大概操作流程是:用戶在APP上選好要購買的商品察藐,點(diǎn)擊“立即購買”,跳轉(zhuǎn)到訂單詳細(xì)頁面舟扎。選擇支付方式分飞,點(diǎn)擊“確定支付”跳轉(zhuǎn)到支付寶APP,付款完成后睹限,跳轉(zhuǎn)回APP譬猫,完成支付。這個(gè)過程羡疗,當(dāng)用戶點(diǎn)擊“確定支付”時(shí)染服,APP需要調(diào)用商戶后臺(tái)接口。
這時(shí)候就是我們所需要做的事情:先是生成商戶系統(tǒng)一筆未支付的訂單叨恨,獲得商戶訂單ID(商戶系統(tǒng)生成)和訂單的一些其他信息柳刮,然后再調(diào)用支付寶的SDK提供的數(shù)字簽名方法,將需要傳給支付寶的信息進(jìn)行加簽痒钝,然后把加簽后的字符串返回給APP秉颗。APP拉起支付寶APP,再把這個(gè)加簽的字符串傳給支付寶送矩,完成支付蚕甥。APP接收到同步通知后,還需要再次調(diào)用商戶后臺(tái)的接口(雖然同步通知也有付款情況栋荸,但需要以后臺(tái)通知為準(zhǔn))菇怀,校驗(yàn)訂單最終的付款情況凭舶。按照支付寶API上所說,當(dāng)完成支付后爱沟,支付寶會(huì)做2個(gè)操作帅霜,一個(gè)是同步返回信息給APP,一個(gè)是異步通知商戶后臺(tái)返回支付狀態(tài)等信息钥顽,并且最終的支付結(jié)果是以異步通知為準(zhǔn)义屏。所以我們還需要考慮到一點(diǎn),就是當(dāng)用戶支付成功之后蜂大,商戶系統(tǒng)暫時(shí)沒有接收到支付寶的異步通知時(shí)。我們需要拿著這個(gè)商戶訂單ID主動(dòng)調(diào)用SDK支付寶的查詢接口蝶怔,去獲取該訂單的支付情況奶浦,并最終返回給APP。這個(gè)查詢的接口應(yīng)該是給APP收到同步通知后踢星,請(qǐng)求商戶系統(tǒng)后臺(tái)進(jìn)行校驗(yàn)的時(shí)候調(diào)用的澳叉。
根據(jù)我們上面思考所得,后臺(tái)只需要對(duì)外提供3個(gè)接口即可
1.用戶點(diǎn)擊“立即購買”時(shí)調(diào)用商戶后臺(tái)接口沐悦,后臺(tái)返回加簽后的訂單信息字符串
2.在支付完成之后成洗,支付寶異步通知商戶后臺(tái)訂單的付款情況
3.在支付完成之后,跳轉(zhuǎn)回APP時(shí)藏否,APP調(diào)用商戶后臺(tái)進(jìn)行最終付款校驗(yàn)
想通想明白之后瓶殃,終于接一下我們要敲代碼了,哈哈哈哈
首先副签,我們來準(zhǔn)備一下需要傳給支付寶SDK的公共基本參數(shù)遥椿,我把參數(shù)放到一個(gè)單獨(dú)的類里,你也可以放到數(shù)據(jù)庫里淆储,代碼如下:
public class AlipayConfig {
// 1.商戶appid
//public static String APPID = "2017...";
//2.私鑰 pkcs8格式的
public static String RSA_PRIVATE_KEY ="MIIEwAIBADANBg.....";
// 3.支付寶公鑰
public static String ALIPAY_PUBLIC_KEY = "MIIBIjANBgkq.....";
// 4.服務(wù)器異步通知頁面路徑 需http://或者h(yuǎn)ttps://格式的完整路徑冠场,不能加?id=123這類自定義參數(shù),必須外網(wǎng)可以正常訪問
public static String notify_url = "http://www.xxx.com/alipay/notify_url.do";
//5.頁面跳轉(zhuǎn)同步通知頁面路徑 需http://或者h(yuǎn)ttps://格式的完整路徑本砰,不能加?id=123這類自定義參數(shù)碴裙,必須外網(wǎng)可以正常訪問 商戶可以自定義同步跳轉(zhuǎn)地址
public static String return_url = "http://www.xxx.com/alipay/return_url.do";
// 6.請(qǐng)求支付寶的網(wǎng)關(guān)地址
public static String URL = "https://openapi.alipay.com/gateway.do";
// 7.編碼
public static String CHARSET = "UTF-8";
// 8.返回格式
public static String FORMAT = "json";
// 9.加密類型
public static String SIGNTYPE = "RSA2";
}
1.實(shí)現(xiàn)第一個(gè)接口:用戶點(diǎn)擊“立即購買”時(shí)調(diào)用商戶后臺(tái)接口,后臺(tái)返回加簽后的訂單信息字符串点额。我把主要的處理邏輯寫在Service層了舔株,Controller層直接調(diào)用就可以,這里就不放Controller層的代碼了
生成商戶訂單的代碼咖楣,我就不放了督笆,這個(gè)根據(jù)各自的業(yè)務(wù)需求來做,生成后訂單之后诱贿,把訂單信息傳進(jìn)來該方法進(jìn)行處理娃肿,返回加簽后的字符串咕缎,直接返回給APP即可,代碼如下:
/**
* 獲取支付寶加簽后臺(tái)的訂單信息字符串
*
* @param request
* @return
*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public String getAliPayOrderStr(OrderTest orderTest) {
//最終返回加簽之后的料扰,app需要傳給支付寶app的訂單信息字符串
String orderString = "";
logger.info("==================支付寶下單,商戶訂單號(hào)為:"+orderTest.getOutTradeNo());
//創(chuàng)建商戶支付寶訂單(因?yàn)樾枰涗浢看沃Ц秾氈Ц兜挠涗浶畔⑵竞溃瑔为?dú)存一個(gè)表跟商戶訂單表關(guān)聯(lián),以便以后查證)
AlipaymentOrder alipaymentOrder=new AlipaymentOrder();
alipaymentOrder.setClubOrderId(orderTest.getId().toString());//商家訂單主鍵
alipaymentOrder.setOutTradeNo(orderTest.getOutTradeNo());//商戶訂單號(hào)
alipaymentOrder.setTradeStatus((byte) 0);//交易狀態(tài)
alipaymentOrder.setTotalAmount(Double.parseDouble(orderTest.getTotalAmount()));//訂單金額
alipaymentOrder.setReceiptAmount(0.00);//實(shí)收金額
alipaymentOrder.setInvoiceAmount(0.00);//開票金額
alipaymentOrder.setBuyerPayAmount(0.00);//付款金額
alipaymentOrder.setRefundFee(0.00); //總退款金額
try{
//實(shí)例化客戶端(參數(shù):網(wǎng)關(guān)地址晒杈、商戶appid嫂伞、商戶私鑰、格式拯钻、編碼帖努、支付寶公鑰、加密類型)粪般,為了取得預(yù)付訂單信息
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID,
AlipayConfig.RSA_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET,
AlipayConfig.ALIPAY_PUBLIC_KEY,AlipayConfig.SIGNTYPE);
//實(shí)例化具體API對(duì)應(yīng)的request類,類名稱和接口名稱對(duì)應(yīng),當(dāng)前調(diào)用接口名稱:alipay.trade.app.pay
AlipayTradeAppPayRequest ali_request = new AlipayTradeAppPayRequest();
//SDK已經(jīng)封裝掉了公共參數(shù)拼余,這里只需要傳入業(yè)務(wù)參數(shù)。以下方法為sdk的model入?yún)⒎绞? AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
//業(yè)務(wù)參數(shù)傳入,可以傳很多亩歹,參考API
//model.setPassbackParams(URLEncoder.encode(request.getBody().toString())); //公用參數(shù)(附加數(shù)據(jù))
model.setBody(orderTest.getBody()); //對(duì)一筆交易的具體描述信息匙监。如果是多種商品,請(qǐng)將商品描述字符串累加傳給body小作。
model.setSubject(orderTest.getSubjecy()); //商品名稱
model.setOutTradeNo(orderTest.getOutTradeNo()); //商戶訂單號(hào)(自動(dòng)生成)
// model.setTimeoutExpress("30m"); //交易超時(shí)時(shí)間
model.setTotalAmount(orderTest.getTotalAmount()); //支付金額
model.setProductCode("QUICK_MSECURITY_PAY"); //銷售產(chǎn)品碼(固定值)
ali_request.setBizModel(model);
logger.info("====================異步通知的地址為:"+alipayment.getNotifyUrl());
ali_request.setNotifyUrl(AlipayConfig.notify_url); //異步回調(diào)地址(后臺(tái))
ali_request.setReturnUrl(AlipayConfig.return_url); //同步回調(diào)地址(APP)
// 這里和普通的接口調(diào)用不同亭姥,使用的是sdkExecute
AlipayTradeAppPayResponse alipayTradeAppPayResponse = alipayClient.sdkExecute(ali_request); //返回支付寶訂單信息(預(yù)處理)
orderString=alipayTradeAppPayResponse.getBody();//就是orderString 可以直接給APP請(qǐng)求,無需再做處理顾稀。
this.createAlipayMentOrder(alipaymentOrder);//創(chuàng)建新的商戶支付寶訂單
} catch (AlipayApiException e) {
e.printStackTrace();
logger.info("與支付寶交互出錯(cuò)达罗,未能生成訂單,請(qǐng)檢查代碼础拨!");
}
return orderString;
}
2.實(shí)現(xiàn)第二個(gè)接口:在支付完成之后氮块,支付寶異步通知商戶后臺(tái)訂單的付款情況,這個(gè)是支付寶每隔一段時(shí)間來訪問一次的接口诡宗,直到你返回success,才會(huì)停止訪問滔蝉,這里我分了2個(gè)地方進(jìn)行調(diào)用
/**
* 支付寶支付成功后.異步請(qǐng)求該接口
* @param request
* @return
* @throws IOException
*/
@RequestMapping(value="/notify_url",method=RequestMethod.POST)
@ResponseBody
public String notify(HttpServletRequest request,HttpServletResponse response) throws IOException {
logger.info("==================支付寶異步返回支付結(jié)果開始");
//1.從支付寶回調(diào)的request域中取值
//獲取支付寶返回的參數(shù)集合
Map<String, String[]> aliParams = request.getParameterMap();
//用以存放轉(zhuǎn)化后的參數(shù)集合
Map<String, String> conversionParams = new HashMap<String, String>();
for (Iterator<String> iter = aliParams.keySet().iterator(); iter.hasNext();) {
String key = iter.next();
String[] values = aliParams.get(key);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
}
// 亂碼解決,這段代碼在出現(xiàn)亂碼時(shí)使用塔沃。如果mysign和sign不相等也可以使用這段代碼轉(zhuǎn)化
// valueStr = new String(valueStr.getBytes("ISO-8859-1"), "uft-8");
conversionParams.put(key, valueStr);
}
logger.info("==================返回參數(shù)集合:"+conversionParams);
String status=alipayMentOrderService.notify(conversionParams);
return status;
}
/**
* 支付寶異步請(qǐng)求邏輯處理
* @param request
* @return
* @throws IOException
*/
public String notify(Map<String, String> conversionParams){
logger.info("==================支付寶異步請(qǐng)求邏輯處理");
//簽名驗(yàn)證(對(duì)支付寶返回的數(shù)據(jù)驗(yàn)證蝠引,確定是支付寶返回的)
boolean signVerified = false;
try {
//調(diào)用SDK驗(yàn)證簽名
signVerified = AlipaySignature.rsaCheckV1(conversionParams, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.CHARSET, AlipayConfig.SIGNTYPE);
} catch (AlipayApiException e) {
logger.info("==================驗(yàn)簽失敗 !");
e.printStackTrace();
}
//對(duì)驗(yàn)簽進(jìn)行處理
if (signVerified) {
//驗(yàn)簽通過
//獲取需要保存的數(shù)據(jù)
String appId=conversionParams.get("app_id");//支付寶分配給開發(fā)者的應(yīng)用Id
String notifyTime=conversionParams.get("notify_time");//通知時(shí)間:yyyy-MM-dd HH:mm:ss
String gmtCreate=conversionParams.get("gmt_create");//交易創(chuàng)建時(shí)間:yyyy-MM-dd HH:mm:ss
String gmtPayment=conversionParams.get("gmt_payment");//交易付款時(shí)間
String gmtRefund=conversionParams.get("gmt_refund");//交易退款時(shí)間
String gmtClose=conversionParams.get("gmt_close");//交易結(jié)束時(shí)間
String tradeNo=conversionParams.get("trade_no");//支付寶的交易號(hào)
String outTradeNo = conversionParams.get("out_trade_no");//獲取商戶之前傳給支付寶的訂單號(hào)(商戶系統(tǒng)的唯一訂單號(hào))
String outBizNo=conversionParams.get("out_biz_no");//商戶業(yè)務(wù)號(hào)(商戶業(yè)務(wù)ID蛀柴,主要是退款通知中返回退款申請(qǐng)的流水號(hào))
String buyerLogonId=conversionParams.get("buyer_logon_id");//買家支付寶賬號(hào)
String sellerId=conversionParams.get("seller_id");//賣家支付寶用戶號(hào)
String sellerEmail=conversionParams.get("seller_email");//賣家支付寶賬號(hào)
String totalAmount=conversionParams.get("total_amount");//訂單金額:本次交易支付的訂單金額螃概,單位為人民幣(元)
String receiptAmount=conversionParams.get("receipt_amount");//實(shí)收金額:商家在交易中實(shí)際收到的款項(xiàng),單位為元
String invoiceAmount=conversionParams.get("invoice_amount");//開票金額:用戶在交易中支付的可開發(fā)票的金額
String buyerPayAmount=conversionParams.get("buyer_pay_amount");//付款金額:用戶在交易中支付的金額
String tradeStatus = conversionParams.get("trade_status");// 獲取交易狀態(tài)
//支付寶官方建議校驗(yàn)的值(out_trade_no鸽疾、total_amount吊洼、sellerId、app_id)
AlipaymentOrder alipaymentOrder=this.selectByOutTradeNo(outTradeNo);
if(alipaymentOrder!=null&&totalAmount.equals(alipaymentOrder.getTotalAmount().toString())&&AlipayConfig.APPID.equals(appId)){
//修改數(shù)據(jù)庫支付寶訂單表(因?yàn)橐4婷看沃Ц秾毞祷氐男畔⒌綌?shù)據(jù)庫里制肮,以便以后查證)
alipaymentOrder.setNotifyTime(dateFormat(notifyTime));
alipaymentOrder.setGmtCreate(dateFormat(gmtCreate));
alipaymentOrder.setGmtPayment(dateFormat(gmtPayment));
alipaymentOrder.setGmtRefund(dateFormat(gmtRefund));
alipaymentOrder.setGmtClose(dateFormat(gmtClose));
alipaymentOrder.setTradeNo(tradeNo);
alipaymentOrder.setOutBizNo(outBizNo);
alipaymentOrder.setBuyerLogonId(buyerLogonId);
alipaymentOrder.setSellerId(sellerId);
alipaymentOrder.setSellerEmail(sellerEmail);
alipaymentOrder.setTotalAmount(Double.parseDouble(totalAmount));
alipaymentOrder.setReceiptAmount(Double.parseDouble(receiptAmount));
alipaymentOrder.setInvoiceAmount(Double.parseDouble(invoiceAmount));
alipaymentOrder.setBuyerPayAmount(Double.parseDouble(buyerPayAmount));
switch (tradeStatus) // 判斷交易結(jié)果
{
case "TRADE_FINISHED": // 交易結(jié)束并不可退款
alipaymentOrder.setTradeStatus((byte) 3);
break;
case "TRADE_SUCCESS": // 交易支付成功
alipaymentOrder.setTradeStatus((byte) 2);
break;
case "TRADE_CLOSED": // 未付款交易超時(shí)關(guān)閉或支付完成后全額退款
alipaymentOrder.setTradeStatus((byte) 1);
break;
case "WAIT_BUYER_PAY": // 交易創(chuàng)建并等待買家付款
alipaymentOrder.setTradeStatus((byte) 0);
break;
default:
break;
}
int returnResult=this.updateByPrimaryKey(alipaymentOrder); //更新交易表中狀態(tài)
if(tradeStatus.equals("TRADE_SUCCESS")) { //只處理支付成功的訂單: 修改交易表狀態(tài),支付成功
if(returnResult>0){
return "success";
}else{
return "fail";
}
}else{
return "fail";
}
}else{
logger.info("==================支付寶官方建議校驗(yàn)的值(out_trade_no冒窍、total_amount递沪、sellerId、app_id),不一致综液!返回fail");
return"fail";
}
} else { //驗(yàn)簽不通過
logger.info("==================驗(yàn)簽不通過 款慨!");
return "fail";
}
}
3.實(shí)現(xiàn)第三個(gè)接口:在支付完成之后,跳轉(zhuǎn)回APP時(shí)谬莹,APP調(diào)用商戶后臺(tái)進(jìn)行最終付款校驗(yàn)檩奠。我把主要的處理邏輯寫在Service層了,Controller層直接調(diào)用就可以附帽,這里就不放Controller層的代碼了埠戳。
微信支付
調(diào)用微信的支付接口,參考微信提供的 api使用了微信的統(tǒng)一下單接口和查詢支付狀態(tài)接口
每個(gè)接口需要的參數(shù)放入到 map 中使用微信提供的 sdk 轉(zhuǎn)成 XML 字符串士葫,httpClient
遠(yuǎn)程提交參數(shù)和接收結(jié)果
具體實(shí)現(xiàn)流程
1.二維碼:前端使用qrious框架,導(dǎo)入js文件
2.共有10個(gè)接口,而我只負(fù)責(zé)兩個(gè)
一個(gè)請(qǐng)求支付地址接口,另一個(gè)查詢支付接口
3.用httpClient技術(shù),封裝了一個(gè)工具類
1.封裝請(qǐng)求內(nèi)容
2通過微信的工具類 將封裝參數(shù)的map轉(zhuǎn)為xml的字符串
3設(shè)置https的請(qǐng)求
4發(fā)送post
5接受到返回的xml字符串,通過微信工具類轉(zhuǎn)為map
支付流程
我們用的支付模塊是用的微信,當(dāng)用戶提交訂單后,我們會(huì)根據(jù)用戶提交的訂單,通過第三方的算法生成一個(gè)二維碼,顯示到前臺(tái),服務(wù)端時(shí)刻監(jiān)控這個(gè)二維碼, 如果用戶到了二維碼頁面一直未支付乞而,或是關(guān)掉了支付頁面,我們的代碼會(huì)一直循環(huán)調(diào)用微信接口慢显,這樣會(huì)對(duì)程序造成很大的壓力。所以我們要加一個(gè)時(shí)間限制或是循環(huán)次數(shù)限制欠啤,當(dāng)超過時(shí)間或次數(shù)時(shí)荚藻,跳出循環(huán)。如果用戶成功付款,則api返回過來一個(gè)支付成功的狀態(tài),我們根據(jù)這個(gè)成功的狀態(tài),做出對(duì)該訂單的一個(gè)后續(xù)狀態(tài)的改變,如果支付失敗,則向前臺(tái)返回一個(gè)支付失敗的頁面,我們是怎么處理之后的日志的問題呢?
(1)在用戶下訂單時(shí)洁段,判斷如果為微信支付应狱,就想支付日志表添加一條記錄,信息包括支付總金額祠丝、訂單ID(多個(gè))疾呻、用戶ID 、下單時(shí)間等信息写半,支付狀態(tài)為0(未支付)
(2)生成的支付日志對(duì)象放入redis中岸蜗,以用戶ID作為key,這樣在生成支付二維碼時(shí)就可以從redis中提取支付日志對(duì)象中的金額和訂單號(hào)叠蝇。
(3)當(dāng)用戶支付成功后璃岳,修改支付日志的支付狀態(tài)為1(已支付),并記錄微信傳遞給我們的交易流水號(hào)悔捶。根據(jù)訂單ID(多個(gè))修改訂單的狀態(tài)為2(已付款)铃慷。
修改好相應(yīng)的日志之后,就可以根據(jù)相應(yīng)的記錄做其他的操作了…..
18.分頁技術(shù)使用什么pagehelper
Maven依賴
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>latest version</version>
</dependency>
這是一個(gè)github上的開源項(xiàng)目,可以方便的完成Java web項(xiàng)目中的翻頁問題
使用Pageahelper
1.配置攔截器插件
在mybatis的配置文件中配置攔截器插件
<!--
plugins在配置文件中的位置必須符合要求蜕该,否則會(huì)報(bào)錯(cuò)犁柜,順序如下:
properties?, settings?,
typeAliases?, typeHandlers?,
objectFactory?,objectWrapperFactory?,
plugins?,
environments?, databaseIdProvider?, mappers?
-->
<plugins>
<!-- com.github.pagehelper為PageHelper類所在包名 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 使用下面的方式配置參數(shù),后面會(huì)有所有的參數(shù)介紹 -->
<property name="param1" value="value1"/>
</plugin>
</plugins>
2.在java類中使用PageInfo來包裝查詢結(jié)果
public String getEmps(@RequestParam(value = "pn",defaultValue="1")Integer pn,
Model model){
PageHelper .startPage(pn,5);
List<Employee> list = employeeService.getAll();
//pageinfo里面封裝了詳細(xì)的信息堂淡,包括我們查詢出來的數(shù)據(jù)馋缅。在構(gòu)造函數(shù)里面的第二個(gè)參數(shù)扒腕,是每頁連續(xù)顯示數(shù)量
PageInfo page = new PageInfo(list,5);
model.addAttribute("pageInfo",page);
return "list";
}
使用pageinfo來包裝查詢出來的結(jié)果,再使用model返回給頁面股囊。
在頁面我們可以使用page.getNativegatpageNums()方法來獲取連續(xù)顯示頁面數(shù)
19.百度地圖api的使用
創(chuàng)建一個(gè)名為"allmap"地圖實(shí)例;
var map = new BMap.Map("allmap");
<div id="allmap"
style="width: 1100px;height: 500px; margin-top: 10px;">
</div>在你想調(diào)用的位置調(diào)用他就可以
如何在頁面中調(diào)用百度地圖袜匿,直接在你想要插入的頁面上調(diào)用百度地圖代碼即可
百度地圖調(diào)用API地址:http://api.map.baidu.com/lbsapi/creatmap/index.html
1.設(shè)置定位中心:直接搜索你要找的位置即可。
調(diào)用百度地圖代碼
2.設(shè)置地圖:設(shè)置地圖樣式稚疹,如大小居灯,顯示,功能等内狗。
3.添加標(biāo)注:添加你要標(biāo)注的地方怪嫌,自定義坐標(biāo)位置
4.獲取代碼:點(diǎn)擊獲取代碼即可,在你要插入百度地圖的地方出入百度地圖代碼
只要插入部分的代碼就行柳沙。
20.購物車功能實(shí)現(xiàn)session和cookie區(qū)別
1. cookie
cookie是由服務(wù)器產(chǎn)生岩灭,存儲(chǔ)在客戶端的一段信息。它定義了一種Web服務(wù)器在客戶端存儲(chǔ)和返回信息的機(jī)制赂鲤,cookie文件它包含域噪径、路徑、生存期数初、和由服務(wù)器設(shè)置的變量值等內(nèi)容找爱。當(dāng)用戶以后訪問同一個(gè)Web服務(wù)器時(shí),瀏覽器會(huì)把cookie原樣發(fā)送給服務(wù)器泡孩。通過讓服務(wù)器讀取原先保存到客戶端的信息车摄,網(wǎng)站能夠?yàn)闉g覽者提供一系列的方便,例如在線交易過程中標(biāo)識(shí)用戶身份仑鸥、安全要求不高的場(chǎng)合避免用戶重復(fù)輸入名字和密碼吮播、門戶網(wǎng)站的主頁定制、有針對(duì)性地投放廣告等等眼俊。利用cookie的特性意狠,大大擴(kuò)展了WEB應(yīng)用程序的功能,不僅可以建立服務(wù)器與客戶機(jī)的聯(lián)系泵琳,因?yàn)閏ookie可以由服務(wù)器定制摄职,因此還可以將購物信息生成cookie值存放在客戶端,從而實(shí)現(xiàn)購物車的功能获列。用基于cookie的方式實(shí)現(xiàn)服務(wù)器與瀏覽器之間的會(huì)話或購物車谷市,有以下特點(diǎn):
cookie存儲(chǔ)在客戶端,且占用很少的資源击孩,瀏覽器允許存放300個(gè)cookie迫悠,每個(gè)cookie的大小為4KB,足以滿足購物車的要求巩梢,同時(shí)也減輕了服務(wù)器的負(fù)荷创泄;
cookie為瀏覽器所內(nèi)置艺玲,使用方便。即使用戶不小心關(guān)閉了瀏覽器窗口鞠抑,只要在cookie定義的有效期內(nèi)饭聚,購物車中的信息也不會(huì)丟失;
cookie不是可執(zhí)行文件搁拙,所以不會(huì)以任何方式執(zhí)行秒梳,因此也不會(huì)帶來病毒或攻擊用戶的系統(tǒng);
基于cookie的購物車要求用戶瀏覽器必須支持并設(shè)置為啟用cookie箕速,否則購物車則失效酪碘;
存在著關(guān)于cookie侵犯訪問者隱私權(quán)的爭(zhēng)論,因此有些用戶會(huì)禁止本機(jī)的cookie功能盐茎。
2. session
session是實(shí)現(xiàn)購物車的另一種方法兴垦。session提供了可以保存和跟蹤用戶的狀態(tài)信息的功能,使當(dāng)前用戶在session中定義的變量和對(duì)象能在頁面之間共享字柠,但是不能為應(yīng)用中其他用戶所訪問探越,它與cookie最重大的區(qū)別是,session將用戶在會(huì)話期間的私有信息存儲(chǔ)在服務(wù)器端窑业,提高了安全性扶关。在服務(wù)器生成session后,客戶端會(huì)生成一個(gè)sessionid識(shí)別號(hào)保存在客戶端数冬,以保持和服務(wù)器的同步。這個(gè)sessionid是只讀的搀庶,如果客戶端禁止cookie功能拐纱,session會(huì)通過在URL中附加參數(shù),或隱含在表單中提交等其他方式在頁面間傳送哥倔。因此利用session實(shí)施對(duì)用戶的管理則更為安全秸架、有效。
同樣咆蒿,利用session也能實(shí)現(xiàn)購物車东抹,這種方式的特點(diǎn)是:
session用新的機(jī)制保持與客戶端的同步,不依賴于客戶端設(shè)置沃测;
與cookie相比缭黔,session是存儲(chǔ)在服務(wù)器端的信息,因此顯得更為安全蒂破,因此可將身份標(biāo)示馏谨,購物等信息存儲(chǔ)在session中;
session會(huì)占用服務(wù)器資源附迷,加大服務(wù)器端的負(fù)載惧互,尤其當(dāng)并發(fā)用戶很多時(shí)哎媚,會(huì)生成大量的session,影響服務(wù)器的性能喊儡;
因?yàn)閟ession存儲(chǔ)的信息更敏感拨与,而且是以文件形式保存在服務(wù)器中,因此仍然存在著安全隱患艾猜。
3. 結(jié)合數(shù)據(jù)庫的方式
這也是目前較普遍的模式买喧,在這種方式中,數(shù)據(jù)庫承擔(dān)著存儲(chǔ)購物信息的作用箩朴,session或cookie則用來跟蹤用戶岗喉。這種方式具有以下特點(diǎn):
數(shù)據(jù)庫與cookie分別負(fù)責(zé)記錄數(shù)據(jù)和維持會(huì)話,能發(fā)揮各自的優(yōu)勢(shì)炸庞,使安全性和服務(wù)器性能都得到了提高钱床;
每一個(gè)購物的行為,都要直接建立與數(shù)據(jù)庫的連接埠居,直至對(duì)表的操作完成后查牌,連接才釋放。當(dāng)并發(fā)用戶很多時(shí)滥壕,會(huì)影響數(shù)據(jù)庫的性能纸颜,因此,這對(duì)數(shù)據(jù)庫的性能提出了更高的要求绎橘;
使cookie維持會(huì)話有賴客戶端的支持胁孙。
各種方式的選擇:
雖然cookie可用來實(shí)現(xiàn)購物車,但必須獲得瀏覽器的支持称鳞,再加上它是存儲(chǔ)在客戶端的信息涮较,極易被獲取,所以這也限制了它存儲(chǔ)更多冈止,更重要的信息狂票。所以一般cookie只用來維持與服務(wù)器的會(huì)話,例如國(guó)內(nèi)最大的當(dāng)當(dāng)網(wǎng)絡(luò)書店就是用cookie保持與客戶的聯(lián)系熙暴,但是這種方式最大的缺點(diǎn)是如果客戶端不支持 cookie就會(huì)使購物車失效闺属。
Session 能很好地與交易雙方保持會(huì)話,可以忽視客戶端的設(shè)置周霉。在購物車技術(shù)中得到了廣泛的應(yīng)用掂器。但session的文件屬性使其仍然留有安全隱患。
結(jié)合數(shù)據(jù)庫的方式雖然在一定程度上解決了上述的問題诗眨,但從上面的例子可以看出:在這種購物流程中涉及到對(duì)數(shù)據(jù)庫表的頻繁操作唉匾,尤其是用戶每選購一次商品,都要與數(shù)據(jù)庫進(jìn)行連接,當(dāng)用戶很多的時(shí)候就加大了服務(wù)器與數(shù)據(jù)庫的負(fù)荷
1巍膘、用戶瀏覽系統(tǒng)厂财,獲取用戶機(jī)器的MAC地址
2、如果用戶購買物品峡懈,添加到數(shù)據(jù)庫里面璃饱,同時(shí)插入機(jī)器的MAC地址,也是用戶的ID標(biāo)示
3肪康、如果用戶登錄系統(tǒng)荚恶,用用戶真實(shí)的ID,更新當(dāng)前機(jī)器的MAC對(duì)應(yīng)的記錄磷支。
4谒撼、如果結(jié)帳的話,更新用戶的id雾狈,刪除購物車?yán)锩娴臇|西
5廓潜、用戶沒有登錄,購物車記錄根據(jù)MAC讀取記錄善榛,如果登錄系統(tǒng)根據(jù)用戶的ID辩蛋,讀取記錄
21.redis的了解
概念:
1、Redis是一個(gè)高性能key-value,它是基于內(nèi)存操作的移盆,它是一個(gè)key-value的非關(guān)系型數(shù)據(jù)庫悼院。
2、可以作為Nosql數(shù)據(jù)庫咒循,告訴緩存据途,消息隊(duì)列的代理。
3叙甸、支持的數(shù)據(jù)類型:字符串昨凡,哈希,列表(list)蚁署,集合,有序集合蚂四,位圖光戈。
對(duì)比:
Memcache是一個(gè)純內(nèi)存數(shù)據(jù)庫,不能夠持久化遂赠,只支持String數(shù)據(jù)類型
優(yōu)點(diǎn):
1久妆、讀寫性能好,但是讀的效率高于寫的效率跷睦,但是可以使用Redis+ssdb+Lua腳本聯(lián)合使用筷弦,因?yàn)镽edis和ssdb共用一套客戶端,即./Redis-cli,同樣應(yīng)用于ssdb中烂琴,所以啟動(dòng)Redis后就可以拿到ssdb中的數(shù)據(jù)爹殊,ssdb寫的效率大于讀的效率,即Redis作為讀的奸绷,ssdb作為寫的
2梗夸、支持?jǐn)?shù)據(jù)的持久化
3、支持主從復(fù)制号醉,主機(jī)回自動(dòng)將數(shù)據(jù)同步到從機(jī)反症,可以進(jìn)行讀寫分離
4、數(shù)據(jù)結(jié)構(gòu)豐富
缺點(diǎn):
1械姻、Redis不具備自動(dòng)容錯(cuò)和恢復(fù)功能(從機(jī)移袍,主機(jī)宕機(jī)以后闷尿,會(huì)導(dǎo)致前端部分讀寫請(qǐng)求失敗,需要等待機(jī)器的重啟)
Redis的主從復(fù)制-->全量復(fù)制
1胞谈、復(fù)制過程中,主機(jī)或fork一個(gè)子進(jìn)程對(duì)內(nèi)存做一份快照士嚎,并將子進(jìn)程的內(nèi)存快照保存為文件呜魄,發(fā)送給從機(jī),然而在該過程總需要確保主機(jī)有足夠的空余內(nèi)存莱衩,若快照文件比較大爵嗅,對(duì)集群的服務(wù)能力會(huì)產(chǎn)生較大的影響
2、在主從復(fù)制的過程中有從機(jī)新加入集群或者從機(jī)和主機(jī)出現(xiàn)網(wǎng)絡(luò)波動(dòng)(斷開連接)笨蚁,都會(huì)造成主機(jī)和從機(jī)之間的一次全量復(fù)制睹晒,這對(duì)實(shí)際的系統(tǒng)運(yùn)營(yíng)造成了不必要的麻煩
3、Redis較難支持在線擴(kuò)容括细,在集群容量達(dá)到上限的時(shí)候伪很,在線擴(kuò)容會(huì)非常復(fù)雜,為解決這一問題奋单,運(yùn)維人員在系統(tǒng)上線的時(shí)候必須確保有足夠的空間锉试,否則會(huì)對(duì)資源造成很大的浪費(fèi)
Redis的應(yīng)用場(chǎng)景:
項(xiàng)目中的首頁信息要存放在Redis中,因?yàn)樵L問頻率高览濒,允許有緩存的數(shù)據(jù)呆盖。
22.微服務(wù)
https://cloud.tencent.com/developer/article/1159826
23.solr搜索服務(wù)
solr是一個(gè)開源搜索平臺(tái),用于構(gòu)建搜索應(yīng)用程序贷笛。 它建立在Lucene(全文搜索引擎)之上应又。 Solr是企業(yè)級(jí)的,快速的和高度可擴(kuò)展的乏苦。 使用Solr構(gòu)建的應(yīng)用程序非常復(fù)雜株扛,可提供高性能。
為了在CNET網(wǎng)絡(luò)的公司網(wǎng)站上添加搜索功能,Yonik Seely于2004年創(chuàng)建了Solr洞就。并在2006年1月盆繁,它成為Apache軟件基金會(huì)下的一個(gè)開源項(xiàng)目。并于2016年發(fā)布最新版本Solr 6.0奖磁,支持并行SQL查詢的執(zhí)行改基。
Solr可以和Hadoop一起使用。由于Hadoop處理大量數(shù)據(jù)咖为,Solr幫助我們從這么大的源中找到所需的信息秕狰。不僅限于搜索,Solr也可以用于存儲(chǔ)目的躁染。像其他NoSQL數(shù)據(jù)庫一樣鸣哀,它是一種非關(guān)系數(shù)據(jù)存儲(chǔ)和處理技術(shù)。
總之吞彤,Solr是一個(gè)可擴(kuò)展的我衬,可部署,搜索/存儲(chǔ)引擎饰恕,優(yōu)化搜索大量以文本為中心的數(shù)據(jù)挠羔。
Apache Solr特點(diǎn)
Solr是Lucene的Java API的包裝。因此埋嵌,使用Solr破加,可以利用Lucene的所有功能。 讓我們來看看Solr的一些最突出的特點(diǎn) -
Restful APIs ? 要與Solr通信雹嗦,并非一定需要有Java編程技能范舀。相反,您可以使用restful服務(wù)與它通信了罪《Щ罚可使用文件格式(如XML,JSON和.CSV)在Solr中作為輸入文檔泊藕,并以相同的文件格式獲取結(jié)果辅辩。
全文搜索 - Solr提供了全文搜索所需的所有功能,例如令牌娃圆,短語汽久,拼寫檢查,通配符和自動(dòng)完成踊餐。
企業(yè)準(zhǔn)備 - 根據(jù)企業(yè)/組織的需要,Solr可以部署在任何類型的系統(tǒng)(大或小)臀稚,如獨(dú)立吝岭,分布式,云等。
靈活和可擴(kuò)展 - 通過擴(kuò)展Java類并相應(yīng)配置窜管,可以輕松地定制Solr的組件散劫。
NoSQL數(shù)據(jù)庫 - Solr也可以用作大數(shù)據(jù)量級(jí)的NOSQL數(shù)據(jù)庫,可以沿著集群分布搜索任務(wù)幕帆。
管理界面 - Solr提供了一個(gè)易于使用获搏,用戶友好,功能強(qiáng)大的用戶界面失乾,使用它可以執(zhí)行所有可能的任務(wù)常熙,如管理日志,添加碱茁,刪除裸卫,更新和搜索文檔。
高度可擴(kuò)展 - 在使用Solr與Hadoop時(shí)纽竣,我們可以通過添加副本來擴(kuò)展其容量墓贿。
以文本為中心并按相關(guān)性排序 - Solr主要用于搜索文本文檔,結(jié)果根據(jù)與用戶查詢的相關(guān)性按順序傳送蜓氨。
與Lucene不同聋袋,在使用Apache Solr時(shí),可不需要具有Java編程技能穴吹。它提供了一個(gè)完整的準(zhǔn)備部署服務(wù)幽勒,以構(gòu)建一個(gè)自動(dòng)完成的搜索框,Lucene是不提供的刀荒。 使用Solr可以擴(kuò)展代嗤,分配和管理大規(guī)模(大數(shù)據(jù))應(yīng)用程序的索引。
Lucene在搜索應(yīng)用程序
Lucene是簡(jiǎn)單但強(qiáng)大的基于Java的搜索庫缠借。 它可以在任何應(yīng)用程序中用于添加搜索功能干毅。 Lucene是一個(gè)可擴(kuò)展的高性能庫,用于索引和搜索幾乎任何類型的文本泼返。 Lucene庫提供任何搜索應(yīng)用程序所需的核心操作硝逢,例如索引和搜索。
如果有一個(gè)具有大量數(shù)據(jù)的門戶網(wǎng)站或平臺(tái)绅喉,那么我們將很可能需要在門戶/平臺(tái)中提取一個(gè)搜索引擎從巨大的數(shù)據(jù)庫中提取相關(guān)信息渠鸽。Lucene作為任何搜索應(yīng)用程序的核心,提供與索引和搜索相關(guān)的重要操作柴罐。
常用JAVA應(yīng)用架構(gòu)
單一應(yīng)用架構(gòu)
- 當(dāng)網(wǎng)站流量很小時(shí)徽缚,只需一個(gè)應(yīng)用,將所有功能都部署在一起革屠,以減少部署節(jié)點(diǎn)和成本凿试。此時(shí)排宰,用于簡(jiǎn)化增刪改查工作量的數(shù)據(jù)訪問框架(ORM) 是關(guān)鍵。
垂直應(yīng)用架構(gòu)
- 當(dāng)訪問量逐漸增大那婉,單一應(yīng)用增加機(jī)器帶來的加速度越來越小板甘,將應(yīng)用拆成互不相干的幾個(gè)應(yīng)用,以提升效率详炬。此時(shí)盐类,用于加速前端頁面開發(fā)的Web框架(MVC) 是關(guān)鍵。
分布式服務(wù)架構(gòu)
- 當(dāng)垂直應(yīng)用越來越多呛谜,應(yīng)用之間交互不可避免在跳,將核心業(yè)務(wù)抽取出來,作為獨(dú)立的服務(wù)呻率,逐漸形成穩(wěn)定的服務(wù)中心硬毕,使前端應(yīng)用能更快速的響應(yīng)多變的市場(chǎng)需求。此時(shí)礼仗,用于提高業(yè)務(wù)復(fù)用及整合的分布式服務(wù)框架(RPC) 是關(guān)鍵吐咳。
流動(dòng)計(jì)算架構(gòu)
- 當(dāng)服務(wù)越來越多,容量的評(píng)估元践,小服務(wù)資源的浪費(fèi)等問題逐漸顯現(xiàn)韭脊,此時(shí)需增加一個(gè)調(diào)度中心基于訪問壓力實(shí)時(shí)管理集群容量,提高集群利用率单旁。此時(shí)沪羔,用于提高機(jī)器利用率的資源調(diào)度和治理中心(SOA) 是關(guān)鍵。