国产精品久久国产精麻豆99网站,激烈18禁高潮视频免费,老师含紧一点h边做边走视频动漫,双乳被一左一右的吸着

個人開發(fā)者如何使用支付寶沙箱開發(fā)支付功能(個人開發(fā)者如何使用支付寶沙箱開發(fā)支付功能)

一般來說支付功能都是通過公司申請賬戶進行開發(fā)、測試。如果是個人想了解學(xué)習(xí)一下,可以使用支付寶提供的沙箱功能,做一些基礎(chǔ)功能的學(xué)習(xí)和測試。我查了一圈沒發(fā)現(xiàn)微信支付有類似的功能,如果誰知道請說一下。

準備工作

登錄支付寶的開發(fā)者中心控制臺,如圖:

個人開發(fā)者如何使用支付寶沙箱開發(fā)支付功能(個人開發(fā)者如何使用支付寶沙箱開發(fā)支付功能)

設(shè)置密鑰:

個人開發(fā)者如何使用支付寶沙箱開發(fā)支付功能(個人開發(fā)者如何使用支付寶沙箱開發(fā)支付功能)

個人開發(fā)者如何使用支付寶沙箱開發(fā)支付功能(個人開發(fā)者如何使用支付寶沙箱開發(fā)支付功能)

生成密鑰的工具,如圖:

個人開發(fā)者如何使用支付寶沙箱開發(fā)支付功能(個人開發(fā)者如何使用支付寶沙箱開發(fā)支付功能)

個人開發(fā)者如何使用支付寶沙箱開發(fā)支付功能(個人開發(fā)者如何使用支付寶沙箱開發(fā)支付功能)

下面還有安卓版的支付寶(沙箱版),配合使用。

代碼

接下來我們就可以著手進行開發(fā)。文檔很豐富,大家可以根據(jù)自己的情況選用,我這里就是用Spring Boot,采用老版的支付寶服務(wù)端SDK,簡單地實現(xiàn)一下當(dāng)面付、PC網(wǎng)頁掃碼付、支付寶回調(diào)通知接口(無外網(wǎng)環(huán)境下如何測試)、基于AOP的驗簽、基于hibernate-validator的參數(shù)校驗以及全局異常捕獲。因為只是學(xué)習(xí)功能,所以代碼寫的不是那么規(guī)整,而且不涉及數(shù)據(jù)庫。

之前在工作中,支付作為一個基礎(chǔ)服務(wù),是用Dubbo服務(wù)提供對外接口的,隨著后續(xù)的發(fā)展,出現(xiàn)了一系列的問題:

  1. 一開始是沒有消息驗簽的。
  2. 回調(diào)業(yè)務(wù)系統(tǒng)的接口是HTTP,因為支付服務(wù)不可能也用Dubbo Service的方式調(diào)業(yè)務(wù)系統(tǒng)的接口,這得引入多少業(yè)務(wù)系統(tǒng)的jar啊。這樣一來,就很別扭了,你調(diào)我接口Dubbo Service,我調(diào)你接口HTTP請求。
  3. 業(yè)務(wù)系統(tǒng)都要引入支付服務(wù)的jar包。

所以后來改成HTTP服務(wù)了。

先看一下結(jié)構(gòu)圖:

個人開發(fā)者如何使用支付寶沙箱開發(fā)支付功能(個人開發(fā)者如何使用支付寶沙箱開發(fā)支付功能)

pom.xml引入jar包:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/com.google.guava/guava --> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>29.0-jre</version> </dependency> <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson --> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java --> <dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alipay-sdk-java</artifactId> <version>4.10.145.ALL</version> </dependency> <!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator --> <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>

在實際工作中,微服務(wù)的背景下,要分配給每個業(yè)務(wù)系統(tǒng)一個clientId、一個驗證消息簽名用的密鑰,還有支付渠道ID——支付寶當(dāng)面付、掃碼支付、APP支付,微信小程序支付等等,不同的channelId對應(yīng)不同的處理策略。

我們在工作中支付流程是:

1、用戶發(fā)起支付,業(yè)務(wù)系統(tǒng)搜集訂單信息,通過HTTP請求至支付服務(wù)(這里可以是異步也可以是同步)。

2、支付服務(wù)對消息進行驗簽,校驗參數(shù)合法性,入庫,轉(zhuǎn)發(fā)請求至支付寶或微信(這里可以是異步也可以是同步)。

3、如果是同步,則直接將返回的信息回傳給業(yè)務(wù)系統(tǒng),例如PC網(wǎng)頁掃碼支付,支付寶就返回了<form>標簽的HTML代碼,業(yè)務(wù)系統(tǒng)的前端頁面要嵌進去。如果是當(dāng)面付這種,其實是可以走異步的,按需處理吧。

4、用戶完成支付后,支付寶會有兩個回調(diào)return_url、notify_url告知支付狀態(tài),支付服務(wù)接到結(jié)果后再回調(diào)業(yè)務(wù)系統(tǒng)的接口,回寫支付狀態(tài)。

驗簽的AOP代碼:

@Aspect@Componentpublic class SignAspect { @Pointcut("execution(* org.leo.demo.controller..PayController.*(..))") public void executionPay() { } @Around("executionPay()") public Object doAround(ProceedingJoinPoint pjp) throws Throwable { ServletrequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = Objects.requireNonNull(attributes).getRequest(); // 獲取請求頭 Enumeration<String> enumeration = request.getHeaderNames(); Map<String, String> headerMap = Maps.newHashMap(); while (enumeration.hasMoreElements()) { String name = enumeration.nextElement(); String value = request.getHeader(name); headerMap.put(name, value); } Gson gson = new Gson(); String json = gson.toJson(pjp.getArgs()[0]); String signFromHeader = headerMap.get("sign"); // 每個客戶端的salt應(yīng)該是不同的,此處可以根據(jù)clientId去DB或Redis中取,順便也校驗一下clientId是否存在 String signFronEncrypt = SignUtils.encrypt(json, SignUtils.salt); System.out.print("n請求明文:" json); System.out.print("n請求簽名:" signFromHeader); System.out.println("n加密簽名:" signFronEncrypt); if (signFromHeader.equals(signFronEncrypt)) { Object result = pjp.proceed(); return result; } else { DefaultResult<PayResult> result = new DefaultResult<PayResult>(); result.setCode(999); result.setMsg("驗簽錯誤"); return result; } }}

我在這里使用了AOP而非Filter,僅僅是因為我不喜歡Filter獲取消息體時,inputStream無法傳入Controller,還要額外處理一下。公司里面倒是用Filter?的多,不知道大家在實際工作中是如何處理的。?

消息加密方法:

/** * 真實環(huán)境中,鹽應(yīng)該是一個客戶端分配一個 */ public static final String salt = "111111"; public static String encrypt(String data, String salt) { // 因為只是驗簽,沒必要解密。MD5已經(jīng)被廢棄 return Hashing.sha256().newHasher().putString(data salt, Charsets.UTF_8).hash().toString(); }

消息加密的方法有很多種,而且每個客戶分配的密鑰也必須不同,我這里只是為了展示一下功能,采用了Guava的Hashing.sha256方法,實際工作中要按照要求進行修改。

HTTP請求除了放在body的json消息體外,還要在header上放入固定的sign,postman請求如圖:

個人開發(fā)者如何使用支付寶沙箱開發(fā)支付功能(個人開發(fā)者如何使用支付寶沙箱開發(fā)支付功能)

個人開發(fā)者如何使用支付寶沙箱開發(fā)支付功能(個人開發(fā)者如何使用支付寶沙箱開發(fā)支付功能)

下面是PayController:

@RestController@Validatedpublic class PayController { @Autowired private AliPayFace2FaceService aliPayFace2FaceService; @Autowired private AliPayScan2PayService aliPayScan2PayService; @PostMapping("/pay.do") @ResponseBody public DefaultResult<PayResult> pay(@Valid @RequestBody PayParam param) { System.out.println("Controller:" param.toString()); if (param.getChannelId() == 1) { return aliPayFace2FaceService.pay(param); } else if (param.getChannelId() == 2) { return aliPayScan2PayService.pay(param); } else { DefaultResult<PayResult> result = new DefaultResult<PayResult>(); result.setCode(222); result.setMsg("支付渠道錯誤"); return result; } } @RequestMapping("/alipaynotify.do") @ResponseBody public String alipayNotify(HttpServletRequest req) throws AlipayApiException { Map<String, String> params = new HashMap<String, String>(); Map<String, String[]> requestParams = req.getParameterMap(); for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) { String name = (String) iter.next(); String[] values = (String[]) requestParams.get(name); String valueStr = ""; for (int i = 0; i < values.length; i ) { valueStr = (i == values.length - 1) ? valueStr values[i] : valueStr values[i] ","; } params.put(name, valueStr); } boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type); if (signVerified) { System.out.println("支付寶回調(diào)驗簽通過"); System.out.println(params.toString()); return "success"; } else { System.out.println("支付寶回調(diào)驗簽失敗"); return "error"; } }}

alipayNotify這個方法我們后面再說。支付渠道應(yīng)該有個枚舉類,這里省略了。

Controller里的pay方法有點像策略模式,根據(jù)不同的支付渠道ID,使用對應(yīng)的處理Service。只是我們這里省下了Context類。

對參數(shù)的校驗,這里放在Controller了,大家可以按照公司的開發(fā)規(guī)范,放在Service也可以。處理類代碼如下:

@ControllerAdvicepublic class GlobalExceptionHandler { @ExceptionHandler(value = MethodArgumentNotValidException.class) @ResponseBody public DefaultResult<List<Map<String, String>>> validParam(MethodArgumentNotValidException e) { // 按需重新封裝需要返回的錯誤信息 List<Map<String, String>> errorMsgs = Lists.newArrayList(); // 解析原錯誤信息,封裝后返回,此處返回非法的字段名稱,原始值,錯誤信息 for (FieldError error : e.getBindingResult().getFieldErrors()) { Map<String, String> errorMap = Maps.newLinkedHashMap(); errorMap.put("字段", error.getField()); errorMap.put("消息", error.getDefaultMessage()); errorMap.put("傳入值", error.getRejectedValue().toString()); errorMsgs.add(errorMap); } DefaultResult<List<Map<String, String>>> result = new DefaultResult<List<Map<String, String>>>(); result.setCode(444); result.setMsg("參數(shù)校驗錯誤"); result.setData(errorMsgs); return result; }}

IPayService這個沒什么好說的,就是通用的方法,本例就寫了一個支付接口。

下面是當(dāng)面付的Service,因為是學(xué)習(xí)測試用,所以省略了訂單入庫的代碼:

@Servicepublic class AliPayFace2FaceService implements IPayService { @Override public DefaultResult<PayResult> pay(PayParam param) { System.out.println("Service:" param.toString()); // 應(yīng)從相關(guān)配置和數(shù)據(jù)庫拿這些參數(shù) AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type); AlipayTradePayRequest request = new AlipayTradePayRequest(); AlipayTradePayModel model = new AlipayTradePayModel(); request.setBizModel(model); model.setOutTradeNo(param.getOrderNo()); model.setSubject(param.getSubject()); // 計算金額應(yīng)有專門的工具類實現(xiàn) BigDecimal b1 = new BigDecimal(param.getTotalAmount()); BigDecimal b2 = new BigDecimal(100); BigDecimal bdResult = b1.divide(b2, 2, RoundingMode.DOWN); model.setTotalAmount(bdResult.toString()); model.setAuthCode(param.getAuthCode());// 沙箱錢包中的付款碼 model.setScene("bar_code"); AlipayTradePayResponse response = null; try { response = alipayClient.execute(request); System.out.println(response.getBody()); System.out.println(response.getTradeNo()); DefaultResult<PayResult> result = new DefaultResult<PayResult>(); PayResult payResult = new PayResult(); if (response.getCode().equals("10000")) { payResult.setPayOrderNo(response.getTradeNo()); } else { result.setCode(Integer.valueOf(response.getCode())); result.setMsg(response.getMsg() "。" response.getSubMsg()); } return result; } catch (AlipayApiException e) { DefaultResult<PayResult> result = new DefaultResult<PayResult>(); result.setCode(753); result.setMsg("支付寶異常"); result.setData(null); return result; } }}

其中AlipayConfig里面就是之前我們在支付寶上配置的公鑰、私鑰、網(wǎng)關(guān)地址、APPID、還有我們的回調(diào)接口,這些信息應(yīng)該從配置文件或者DB里面獲取。

支付寶接收的金額是元,小數(shù)點后兩位,也就是只到分了。而我們在工作中,實際上金額存的都是long型,沒有小數(shù)點,直接到分,這里要寫一個專門的工具類處理一下,本例省略了。

付款碼就是沙箱支付寶APP中,“付款”-“查看數(shù)字”。測試的時候要快,因為這段數(shù)字會變。

下面是PC網(wǎng)頁掃碼付的Service:

@Servicepublic class AliPayScan2PayService implements IPayService { @Override public DefaultResult<PayResult> pay(PayParam param) { System.out.println("Service:" param.toString()); AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type); AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest(); alipayRequest.setReturnUrl(AlipayConfig.return_url); alipayRequest.setNotifyUrl(AlipayConfig.notify_url); // 付款金額 BigDecimal b1 = new BigDecimal(param.getTotalAmount()); BigDecimal b2 = new BigDecimal(100); BigDecimal bdResult = b1.divide(b2, 2, RoundingMode.DOWN); // 最晚付款時間。m分鐘,h小時,d天,1c-當(dāng)天(0點關(guān)閉)。 String timeoutExpress = "15m"; String body = ""; DefaultResult<PayResult> result = new DefaultResult<PayResult>(); PayResult payResult = new PayResult(); alipayRequest.setBizContent("{"out_trade_no":"" param.getOrderNo() ""," ""total_amount":"" bdResult ""," ""subject":"" param.getSubject() ""," ""body":"" body ""," ""timeout_express":"" timeoutExpress ""," ""product_code":"FAST_INSTANT_TRADE_PAY"}"); try { String htmlStr = alipayClient.pageExecute(alipayRequest).getBody(); System.out.println(htmlStr); payResult.setHtmlStr(htmlStr); } catch (AlipayApiException e) { result.setCode(753); result.setMsg("支付寶異常"); result.setData(null); } return result; }}

這里返回的是一段HTML代碼(微信支付返回的是一個二維碼),我們拿到之后,可以新建一個HTML文件放進去,直接掃碼支付。

個人開發(fā)者如何使用支付寶沙箱開發(fā)支付功能(個人開發(fā)者如何使用支付寶沙箱開發(fā)支付功能)

掃碼支付后,支付寶會回調(diào)我們提供的return_url和notify_url接口,告知結(jié)果,但是作為個人開發(fā),如果沒有外網(wǎng)IP,該如何驗證一下呢?

先說一下return_url和notify_url的區(qū)別。

掃碼支付完成后,支付網(wǎng)頁會同步跳轉(zhuǎn)到return_url,展示一些信息,這是個get請求,僅發(fā)一次。

而notify_url是由支付寶后端發(fā)起的post請求,如果我們不返回success消息,支付寶會進行重發(fā)。

所以從工作中來說,我們一般是把return_url做一個臨時展示,而在notify_url中進行一些邏輯處理,例如回寫訂單狀態(tài)等操作。

在測試開發(fā)中,因為我們沒有外網(wǎng)IP,網(wǎng)頁會彈出一個信息框,告知地址無法訪問,這時候我們就可以把整個url復(fù)制下來,自己在瀏覽器上訪問一下,從而可以驗證我們的回調(diào)接口是否正常。

版權(quán)聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻,該文觀點僅代表作者本人。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如發(fā)現(xiàn)本站有涉嫌抄襲侵權(quán)/違法違規(guī)的內(nèi)容, 請發(fā)送郵件至 舉報,一經(jīng)查實,本站將立刻刪除。

精品国产一区二区三区av| 欧美xxxx黑人又粗又长精品| 亚洲国产精品久久久久婷婷图片| 中文字幕爆乳julia女教师 | 国产精品沙发午睡系列| 亚洲国产精彩中文乱码av| 国产精品人妻无码77777| 一本大道精品成人免费视频| 荫蒂被男人添a片视频| 国产一区二区三区美女| 久久精品国产亚洲一区二区| 色欲av亚洲一区无码少妇| 亚洲精品无码mv在线观看| 娇妻被朋友日出白浆抖内| 被绑在机器上强行高潮h| 人妻夜夜添夜夜无码av茄子视频| 最近韩国日本免费观看免费| 人人妻人人澡人人爽欧美一区九九| aa片在线观看视频在线播放| 亚洲av永久无码嘿嘿嘿| 久久久无码av精品亚洲a片软件 | 四虎成人精品在永久免费| 女人被爽到呻吟gif动态图| 两个人看的www高清免费中文 | 久久欧美牲大无无码毛片| 国内精品人妻无码久久久影院蜜桃| 国产精品妇女一二三区| 国语精彩对白在线视频| 日文中字乱码一二三区别在哪| 久久99精品国产99久久| 日本19禁啪啪吃奶大尺度| 亚洲色WWW永久网站| 国产精品欧美一区二区三区| 99久久99久久久精品齐齐| 亚洲欧洲无码av不卡在线| 人人添人人澡人人澡人人人人| 色婷婷丁香五月久久综合 | 日本一上一下爱爱免费| 日本少妇人妻xxxxx18免费| 熟女性饥渴一区二区三区| 国产aⅴ激情无码久久久无码|