The realization of Alipay's three party payment function
Alipay payment environment preparation
1.1 sandbox environment preparation
Because we are testing the environment, we first use the sandbox environment provided by Alipay (simulation payment scenario).
We need to generate our private key and public key on Alipay open platform assistant client.
Then we provide our public key to Alipay sandbox environment.
Click this on the sandbox page
Then we need to store Alipay's public key and our private key in the database we get.
Because we use AES2 to encrypt (asymmetric reversible encryption algorithm)
1.2 AES2 encryption algorithm
1 Irreversible encryption algorithm: it can only be encrypted but not decrypted md5 Generally, the login password is processed 2 Reversible encryption algorithm: it can be encrypted or decrypted, and can be divided into whether it is symmetrical, mainly depending on whether the secret key is the same,It's symmetry, otherwise it's asymmetry 2.1 Symmetric encryption algorithm: DES,AES Wait for the database password to be encrypted Ciphertext is s The encryption and decryption algorithm is AES The secret key is k,Plaintext c ; Encryption: s = AES(k,c) decrypt: c = AES(k,s) 2.2 Asymmetric encryption algorithm: RSA Network transmission data encryption Passwords appear in pairs. A private key corresponds to a public key. If you use private key encryption, you can only use the corresponding public key. On the contrary, if public key encryption is used, only the corresponding private key can be used for decryption. Ciphertext is s The encryption and decryption algorithm is RSA Private key is k1 Public key is k2,Plaintext c ; Group 1: private key encryption and public key decryption Encryption: s= rsa(k1,c) decrypt: c=rsa(k2,s) Group I: public key encryption and private key solution Encryption: s= rsa(k2,c) decrypt: c=rsa(k1,s)
Exchange public key
1.3 intranet penetration
Preparation 2: asynchronous notification is a controller method to visit our site in httppost request mode. It is Alipay request from external network payment, so that we can access it by external network.
online: buy servers and domain names
development: intranet penetration nat123 (18) peanut shell (6) natapp (free)
https://natapp.cn/
1) Register and purchase free tunnel token
2) Download the software and create config. In the current directory in the extracted folder INI file
3) In config INI file writes the obtained token in the file
4) Note: because we use the free tunnel of natapp, the token will be changed from time to time
Cannot close when running a successful test project
code implementation
2.1 initiate payment request
2.1.1 packaging tools
<!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java --> <dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alipay-sdk-java</artifactId> <version>3.7.4.ALL</version> </dependency>
Used to set synchronous and asynchronous callback address
package cn.many.pay.constants; public class AlipayConfig { // Server asynchronous notification page path public static String notify_url = "http://xrs456.natappfree.cc/notify"; // Page Jump synchronization notification page path public static String return_url = "http://localhost/success.html"; // Signature method public static String sign_type = "RSA2"; // Character encoding format public static String charset = "utf-8"; // Alipay gateway public static String gatewayUrl = "https://openapi.alipaydev.com/gateway.do"; }
The tool class returned to us is the String type Alipay data package.
We need to return the background to the foreground to display the QR code interface
package cn.many.pay.utils; import cn.many.pay.constants.AlipayConfig; import cn.many.pay.domain.AlipayInfo; import cn.many.pay.domain.PayBill; import com.alipay.api.AlipayClient; import com.alipay.api.DefaultAlipayClient; import com.alipay.api.request.AlipayTradePagePayRequest; public class AlipayUtils { /** * @param info To which merchant * @param bill Payment order * @return Payment request packet- */ public static String pay(AlipayInfo info, PayBill bill){ try { //Get the initialized AlipayClient AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, info.getAppid(), info.getMerchant_private_key(), "json", AlipayConfig.charset, info.getAlipay_public_key(), AlipayConfig.sign_type); //Set request parameters AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest(); alipayRequest.setReturnUrl(AlipayConfig.return_url); alipayRequest.setNotifyUrl(AlipayConfig.notify_url); //Merchant Order No., which is the only order No. in the order system on the merchant's website. It is required. / / it is the unified payment doc No String out_trade_no = bill.getUnionPaySn(); //Payment amount, required String total_amount = bill.getMoney().toString(); //Order name, required String subject = bill.getDigest(); //Product description; can be blank String body = bill.getDigest(); alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\"," + "\"total_amount\":\""+ total_amount +"\"," + "\"subject\":\""+ subject +"\"," + "\"body\":\""+ body +"\"," + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}"); //request String result = alipayClient.pageExecute(alipayRequest).getBody(); return result; } catch (Exception e) { e.printStackTrace(); } return null; } }
2.1.2 implementation of foreground code
adoptOrder.html
Modify the method triggered by the foreground submit order button. At this time, we are in the Ajax result object
The resultObj attribute encapsulates the Alipay packets returned by the AlipayUtils tool class.
That is to say, a form form is used to display Alipay's payment two-dimensional code.
orderSubmit(){ this.$http.post("/adopt/submit",this.order) .then(result=>{ result = result.data; if(result.success){ if(this.order.pay_method==1){ //Get payment data - to ensure that our payment form is the first form, put it first console.log(result.resultObj) $("#payDiv").html(result.resultObj); } }else{ alert(result.message); } }) }
2.1.3 background code implementation
Controller
@RestController @RequestMapping("/adopt") public class AdoptOrderController { @Autowired private IAdoptOrderService orderService; @PostMapping("/submit") public AjaxResult submit(@RequestBody Map<String,Object> params, HttpServletRequest request){ try{ String payData = orderService.submit(params, LoginContext.getLoginInfo(request)); System.out.println(payData); return AjaxResult.me().setResultObj(payData); }catch (Exception e){ e.printStackTrace(); return AjaxResult.me().setSuccess(false).setMessage("Order failed!"+e.getMessage()); } } }
Service
@Override public String submit(Map<String, Object> params, LoginInfo loginInfo) { //Receive parameters Long petId = Long.valueOf(params.get("pet_id").toString()); Long addressId = Long.valueOf(params.get("address_id").toString());//Receiving address: t_ user_ id of address Long payMethod = Long.valueOf(params.get("pay_method").toString());//1 Alipay 2 WeChat 3 UnionPay 0 balance Long serviceMethod = Long.valueOf(params.get("service_method").toString());//Delivery method //1. Change pet status to off shelf Pet pet = petMapper.loadById(petId); pet.setState(0); //2. Bind the buyer User user = userMapper.loadByLoginInfoId(loginInfo.getId()); pet.setUser_id(user.getId()); petMapper.update(pet); //3. Generate order //3.1 saving the adoption order form AdoptOrder order = initAdoptOrder(pet, user); //Tool class generates unique payment order number String unionPaySn = CodeGenerateUtils.generateUnionPaySn(); order.setPaySn(unionPaySn); adoptOrderMapper.save(order); //3.2 save order address table OrderAddress orderAddress =userAddress2AoptAddress(addressId, order); orderAddressMapper.save(orderAddress); //4. Generate payment document PayBill payBill = innitPayBill(payMethod, pet, user, order); payBillMapper.save(payBill); //Call the unified payment interface to complete the payment task return payBillService.pay(payBill); //5. Set order scheduled task @ TODO }
Other calling methods in the Service initialize the table object 𞓜 object attribute replication
/** * Initialize object for payment order * @param payMethod * @param pet * @param user * @param order * @return */ private PayBill innitPayBill(Long payMethod, Pet pet, User user, AdoptOrder order) { PayBill payBill = new PayBill(); payBill.setDigest(order.getDigest()); payBill.setMoney(order.getPrice()); payBill.setUnionPaySn(order.getPaySn()); payBill.setState(0);//To be paid payBill.setLastPayTime(new Date(System.currentTimeMillis()+ PayConstants.LAST_TIME)); payBill.setPayChannel(payMethod);//Payment method from the front desk payBill.setBusinessType(PayConstants.BUSINESS_TYPE_ADOPT);//Order type payBill.setBusinessKey(order.getId());//Query the unique order by order type + order number. Here, the id of order is used payBill.setUser_id(user.getId()); payBill.setShop_id(pet.getShop_id()); payBill.setNickName(user.getUsername()); return payBill; } /** * Convert user address to adoption order address * @param addressId * @param order * @return */ private OrderAddress userAddress2AoptAddress(Long addressId, AdoptOrder order) { OrderAddress orderAddress = new OrderAddress(); UserAddress userAddress = userAddressMapper.loadById(addressId); BeanUtils.copyProperties(userAddress, orderAddress); orderAddress.setId(null); orderAddress.setOrder_id(order.getId()); orderAddress.setOrderSn(order.getOrderSn()); return orderAddress; } /** * Initialize order object * @param pet * @param user * @return */ private AdoptOrder initAdoptOrder(Pet pet, User user) { AdoptOrder order = new AdoptOrder(); order.setDigest("[abstract]adopt"+pet.getName()); order.setPrice(pet.getSaleprice()); order.setOrderSn(CodeGenerateUtils.generateOrderSn(user.getId())); order.setLastConfirmTime(new Date(System.currentTimeMillis()+15*60*1000)); order.setPet_id(pet.getId()); order.setUser_id(user.getId()); order.setShop_id(pet.getShop().getId()); return order; }
Implementation of unified payment interface
All orders call their own business interface - > and then call the unified payment interface in each business interface
The payment interface is only responsible for integrating the implementation of all payment methods and the return value is irrelevant. Business orders only distinguish payment methods
Here we realize the Alipay payment.
@Override public String pay(PayBill payBill) { //It is judged to be empty to prevent the foreground from transmitting empty data if(payBill==null){ throw new BusinessException("Please pay after generating the payment document!!!"); } //Go to the database to query whether there is this payball object PayBill payBill1 = payBillMapper.loadByUnionPaySn(payBill.getUnionPaySn()); if(payBill1==null){ throw new BusinessException("Please pay after generating the payment document!!!"); } //Get the payment method in the payment form 0 balance 1 Alipay 2 WeChat 3 UnionPay Long payChannel = payBill1.getPayChannel(); String resultData = ""; switch(payChannel.intValue()){ case 1 ://Alipay AlipayInfo info = alipayInfoMapper.loadByShopId(payBill1.getShop_id()); resultData = AlipayUtils.pay(info, payBill1); break; case 2 ://WeChat //TODO break; case 3 ://UnionPay //TODO break; default : //0 balance //TODO } return resultData;//Return Alipay packets }
Asynchronous callback
What asynchronous callbacks need to do
->Modify payment doc status
->Modify order status
Background implementation
@RestController public class AlipayController { @Autowired private IPayBillService payBillService; @Autowired private IAlipayInfoService alipayInfoService; @Autowired private IAdoptOrderService adoptOrderService; @PostMapping("/notify") public void notify(HttpServletRequest request){ System.out.println("Alipay asynchronous callback!"); //Get feedback from Alipay POST try { Map<String,String> params = new HashMap<String,String>(); Map<String,String[]> requestParams = request.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] + ","; } valueStr = new String(valueStr); params.put(name, valueStr); } String unionPaySn = params.get("out_trade_no"); PayBill payBill = payBillService.loadByUnionPaySn(unionPaySn); if(payBill != null){ AlipayInfo info = alipayInfoService.getByShopId(payBill.getShop_id()); boolean signVerified = AlipaySignature.rsaCheckV1(params, info.getAlipay_public_key(), AlipayConfig.charset, AlipayConfig.sign_type); //Call SDK to verify signature if(signVerified) {//Validation succeeded //Merchant order number String out_trade_no = unionPaySn; //Alipay transaction number String trade_no = request.getParameter("trade_no"); //Transaction status String trade_status = request.getParameter("trade_status"); if(trade_status.equals("TRADE_FINISHED")){ //User confirmation }else if (trade_status.equals("TRADE_SUCCESS")){ //1. Change payment doc status payBill.setState(1); payBill.setUpdateTime(new Date()); payBillService.update(payBill); String businessType = payBill.getBusinessType(); //2. Modify the corresponding (adopted) order status businesstype businesskey order if(PayConstants.BUSINESS_TYPE_ADOPT.equals(businessType)){//Adoption order Long orderId = payBill.getBusinessKey(); AdoptOrder order = adoptOrderService.getById(orderId); order.setState(1); adoptOrderService.update(order); } } }else {//Validation failed System.out.println("Lao song, don't make trouble"); //Debug and try to write a text function to record whether the program is running normally //String sWord = AlipaySignature.getSignCheckContentV1(params); //AlipayConfig.logResult(sWord); } } } catch (AlipayApiException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } }