IONIC3微信支付 (附后台JAVA)

2023-10-14 18:20
文章标签 java 微信 支付 后台 ionic3

本文主要是介绍IONIC3微信支付 (附后台JAVA),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Ionic3微信支付流程

总结通俗来说微信支付一共两步(最后附全部代码):

1.统一下单(给微信获得微信返回的支付订单号(prepay_id))

解释一下:通俗点讲用户支付钱的时候,需要跳到微信界面,根据一个微信支付订单(微信界面肯定是微信的东西,和自己的项目没有任何关系)来支付钱,这个微信支付订单是微信生成的以下简称vx订单,怎么生成?项目后台根据一系列参数生成一个map,利用map和商户微信支付密钥key根据规则生成签名sign,将sign也放入到此前的map中,再将map转换为xml(格式参考微信支付文档,微信只接受xml),并将此xml通过post请求发送给微信(发送url为微信统一定死的地址,详细接着看下面),不论成功与否都回返回一个xml,若成功xml转map后会取得此次统一下单的终极目的参数prepay_id(预支付id),这个id是统一下单步骤的终极目标,获得此id代表已经成功让微信为用户之后的跳转到微信支付界面付钱生成了一个订单界面,等候用户跳转微信支付。若失败则要查看返回的xml中,returnMsg会提示报的错误,根据错误排查(文章最后会有一系列坑及解决方案)。

注意:此步骤完成不会跳转微信支付接口,仅仅是给微信支付下了个可以理解为预付订单,取得预付订单号prepay_id

统一下单xml示例:

参数举例:商品订单号out_trade_no(unique,并不是vx订单)和钱数total_fee(any)已经用户支付时的客户端ip spbill_create_ip等等一系列参数,参考下图。

以下是必不可少的参数小白解释(具体可参考微信支付文档):

appid(String 32):微信开发平台创建app时取得

mch_id(String 32):商户号id:微信开发平台申请微信支付资格成功时取得。

body(String 32):自己定义对订单商品的描述,比如body = 充值Q币,用户跳到微信支付界面时显示出的充值界面内容即为充值Q币。

Out_trade_no(String 32):商品订单号,此订单号为我们自己生成的订单号,必须唯一。

Total_fee(Int):单位为分! 用户实际支付的金额

Spbill_create_ip(String 16):用户支付时客户端ip

Trader_type(String 16):APP(app支付时定死)

Nonce_str(String 32):随机生成字符串

Notify_url(String 256):用户支付完成后(完成统一下单不会走回调!),微信收到了钱或支付失败都有可能返回一个xml,返回时需要异步的访问我们服务器的一个url地址,此地址作为支付的回调函数,在此地址中处理相应业务逻辑,返回时仅仅会有商品订单号(项目生成),和实际支付金额,实际支付时间有用。

Sign(String 32):生成签名,需要用到商户支付密钥(不是appSecret!)此出生成签名必须与返回给TS的参数中生成签名的方式一致。具体生成方式网上一大堆。

1.调起支付接口

将统一下单取得的prepay_id和其他参数如下等再次(第二次)生成签名,与之前同意下单的签名不相同!将这些参数发送回前台准备进行微信窗口的跳转(调起支付接口),具体操作见下节。

 

注意:timestamp必须为String类型的时间戳!扩展字段为定死。商户订单号outtradeno可不传,为前台处理业务逻辑用。

2.App微信支付资格准备

https://open.weixin.qq.com/cgi-bin/frame?t=home/app_tmpl&lang=zh_CN

1.创建app,注册微信平台相关信息。取得appid,appSecret

2.通过后,开通微信支付资格(商户号申请),具体材料有打包并且已经签名成功的apk

3.取得资格后需要取得的参数为商户号,商户支付密钥。

最终上述步骤取得微信支付实际所需参数有:

APPID:申请的appid

MCH_ID:商户号

KEY:商户号的支付密钥(不是appSecret!)

下面是后台示例此步骤所获得的参数一览:

注意:apk包名和apk的签名一旦绑定不可更改,必须与正式发版后的包名签名一致,否则

会导致出现签名错误(普通错误)。

微信支付插件安装

Cmd: cordova plugin add cordova-plugin-wechat@2.0 --variable wechatappid=xxxxxxxxxxxxxxx

注意:@2.0为版本号可更换 wechatappid为申请的appid(不是商户号!),此处必须与申请微信支付的app的appid一致。

安装完成后,打开confing.xml若如图所示版本号的appid无误则为安装成功。

请求后台进行统一下单

安装成功后,经大量测试,无需引入module等其他操作,直接在要使用微信支付的page.ts开搞。

1.在ts中组件声明前声明一个window对象。

2.直接请求(get/post都可)后台(必须参数:支付金额(注意微信接口的单位为分,此处若为元需处理),用户id(进行支付钱的业务处理需要),订单号可后台生成),后台返回值重新组装一个类似于post请求的对象参数,必须的参数如下图(微信文档有7个必须,经测试有下图5个为必须即可)

  1. 统一下单后台代码示例

注意:

  1. 发送的post请求编码格式一定要统一(UTF-8),否则返回的xml会报body乱码错误
  2. KEY一定要是商户支付密钥不是appSercet!!两者不同。
  3. Total_fee在此处的单位一定是分,不论之前业务处理以元或其他单位,此处必须转换成分。
  4. 订单号一定是唯一的,此处建议取上一个订单号+1,再拼接一个一定位数的随机字符串,在回调函数中直接sub掉一定位数的字符串即可拿到唯一的订单号。
  5. 回调函数不能带参数!
  6. 统一下单的签名方式一定要和调起支付接口参数的签名方式一致!但并不是同一个签名!

因为签名时所需参数多了一个prepay_id!

  1. Timestamp时间戳一定要是string!
  2. 注意发送参数的大小写和非人的下划线变化。Eg:mch_id =>partnerid,nonce_str =>noncestr
  3. 统一下单中返回的xml报错商户订单号重复,解决方法检查发送给微信的商户订单号是否与上次未完成的订单重复,若未重复,等待一天即可(微信取消支付异常的预付订单)。

 

调起支付接口,跳转微信界面。

拿到通过第一步统一下单取得的参数后,将此对象参数通过下图请求发送即可跳转微信界面。

若失败 reason会打印错误(eg:1.普通错误2.用户取消支付返回等)。成功则为完成支付。

注意:

  1. 注意es5和es6的坑,回调函数中的this指向问题,es5 为function(data){}其中的this不是指ts的this而是回调函数中的this 。es6写法为(data)=> this指向为ts中的this。\
  2. 若返回普通错误,说明这个方法本身没问题,且插件安装正确。在统一下单成功的情况下,检查appid / 商户号 /商户支付密钥(不是appSecert)是否和申请通过时的一致!
  3. 若一致可能是申请通过时间未到24小时(一般要在项目开始时申请,不要先做现申请)!

 

 

以下为全部代码:

ts:

//声明window
declare var window;
@Component({selector: 'page-paywaymodal',templateUrl: 'paywaymodal.html',
})
    /*** 微信统一下单*/getWXPayParam(item) {console.log(item.type);let paymentObject = {paymentFee: this.payFee,payer: GlobalConfig.USERINFO.id,paymentType: item.paymentConfigType,paymentConfigName: item.paymentConfigName,paymentConfigId: item.paymentConfigId,paymentStatu: 0,orderIdList: this.orderId};this.httpService.post(this.payUrl, paymentObject).then(data => {if (!data.success) {this.toastAlertService.errorToast(data.message, 1000, "top");} else {console.log(data);let params = {// appid:data.preOrderResult.appid,   partnerid: data.preOrderResult.partnerid,  //商户号prepayid: data.preOrderResult.prepayid,    //预支付id//package: data.preOrderResult.package,noncestr: data.preOrderResult.noncestr,    //随机数timestamp: data.preOrderResult.timestamp,  //时间戳sign: data.preOrderResult.sign,             //签名//appID};this.WXPay(params, data.preOrderResult.outtradeno, data.preOrderResult.paymentFee);}});}
    /*** 调起微信支付接口* @param params */WXPay(params, paymentSn, paymentFee) {window.Wechat.sendPaymentRequest(params, (data) => {this.toastAlertService.successToast(data.message, 2000, "top");this.viewCtrl.dismiss(true);}, (reason) => {this.toastAlertService.successToast("支付失败" + reason, 2000, "middle");this.viewCtrl.dismiss(false);})}

java(serviceImpl):

package com.xxx.common.business;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import com.xxx.common.api.WeChatConfig;
import com.xxx.utils.CommonUtil;
import com.xxxx.utils.HttpRequest;
import com.xxxx.utils.WXPayUtil;
import com.xxxx.weixin.api.WxOrderService;
/*** 微信支付接口(仔细看注解)* @author lu**/
@Service
public class WxOrderServiceImpl implements WxOrderService {/*** ==========================================* 参数:* out_trade_no:订单号 total_fee:支付钱数  body:所购买的物品简介* 注意:*  1.微信预付单:指的是在自己的平台需要和微信进行支付交易生成的一个微信订单,称之为“预付单”*  2.订单:指的是自己的网站平台与用户之间交易生成的订单或消费记录*  3.时间戳一定要转成字符串类型,生成签名方式一定要相同* 微信支付过程:* 1. 生成网站订单或消费记录(一定要有否则保存不了参数处理业务逻辑)* 2. 用户支付 --> 网站在微信平台生成预付单(实际取得的是预支付订单号)* 3. 最终实际根据预付单的信息进行支付* ==========================================*/@Overridepublic Map<String,String> placeOrder(String body,String out_trade_no,String total_fee,HttpServletRequest request) throws Exception {System.out.println("进入产生预付订单api");try {//获取请求用户端实际ipString ip = request.getHeader("x-forwarded-for");if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){ip = request.getHeader("Proxy-Client-IP");}if(ip == null || ip.length()==0 || "unknown".equalsIgnoreCase(ip)){ip = request.getHeader("WL-Proxy-Client-IP");}if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){ip = request.getRemoteAddr();}if(ip.indexOf(",")!=-1){String[] ips = ip.split(",");ip = ips[0].trim();}System.out.println("------请求用户端ip----------"+ip);Map<String, String> params = new HashMap<String, String>();	String total_yuan = "";// new BigDecimal(total_fee).movePointRight(2).toString();//分转元(测试先设支付为分)// 订单总金额,单位为元(测试中先为分)if (StringUtils.isNotEmpty(total_fee)) {total_yuan = new BigDecimal(total_fee).movePointRight(2).toString();//充值碳汇币以元为单位(分转元)3-》300分;// new BigDecimal(recharge_fee).movePointRight(2).toString();//充值碳汇币以元为单位(分转元)}// 用户支付次数过于密集,会在极短的时间内生成累加订单号和生成订单之间会有可能出现重复订单号的问题,故在发送给微信的订单号后面追加随机数,在回调函数中sub掉取出,会大大降低订单的重复率		out_trade_no += CommonUtil.getUUID().substring(0, 3);//3位随机数			params.put("appid",WeChatConfig.APPID);//APPIDparams.put("mch_id",WeChatConfig.MCH_ID);//商户号params.put("body",body);//详情描述params.put("out_trade_no", out_trade_no);//订单号params.put("total_fee", total_yuan);//支付费用(分)//params.put("total_fee", "1");测试用params.put("spbill_create_ip", ip);//用户端实际ipparams.put("trade_type",WeChatConfig.TRADE_TYPE);//交易类型params.put("nonce_str", WXPayUtil.generateNonceStr());//随机字符串params.put("notify_url",WeChatConfig.NOTIFY_URL);//回调urlString sign = WXPayUtil.generateSignature(params,WeChatConfig.KEY);//生成签名params.put("sign", sign);System.out.println("统一下单参数:"+params.toString());String xml = WXPayUtil.mapToXml(params).replace("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>", "").trim();//将所有参数(map)转xml格式,微信方接收方式为xmlString xmlStr = HttpRequest.sendPost(WeChatConfig.PLACEANORDER_URL, xml);//发送post请求"统一下单接口"返回预支付id:prepay_idSystem.out.println("xmlStr显示:" + xmlStr);String prepay_id = "";if (xmlStr.indexOf("SUCCESS") != -1) {  Map<String, String> map = WXPayUtil.xmlToMap(xmlStr);  prepay_id = (String) map.get("prepay_id");//预支付ID  System.out.println("取得预支付id:"+prepay_id);}Map<String, String> payMap = new HashMap<String, String>();payMap.put("appid",WeChatConfig.APPID);  			payMap.put("partnerid",WeChatConfig.MCH_ID);payMap.put("prepayid",prepay_id);//预支付id(预支付提交后微信返回)payMap.put("package",WeChatConfig.PACKAGE);//扩展字段payMap.put("noncestr",WXPayUtil.generateNonceStr());//随机字符串payMap.put("timestamp",WXPayUtil.getCurrentTimestamp()+"");//时间戳String paySign = WXPayUtil.generateSignature(payMap,WeChatConfig.KEY);payMap.put("sign", paySign);//签名(注意:签名方式一定要与统一下单接口使用的一致)payMap.put("outtradeno", out_trade_no.substring(0,out_trade_no.length() - 3));//订单号payMap.put("paymentFee",total_fee);//total_yuan 实际支付金额System.out.println("吊起支付接口参数:"+payMap.toString());return payMap;} catch (Exception e) {e.printStackTrace();}return null;}}

/*** @Description: 微信支付的一些参数配置* @author lu*/ 
public class WeChatConfig {public static final String APPID = "xxxxxxxx";						// 公众账号IDpublic static final String MCH_ID = "xxxxxxxx";						// 商户号public static final String KEY = "xxxxxxxxxx";		// 商户密钥public static final String PACKAGE = "Sign=WXPay";
/*	// APP和网页支付提交用户端ip, Native支付填调用微信支付API的机器IP, 即:服务器ip地址public static final String SPBILL_CREATE_IP = "127.0.0.1";*/// 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。(需要配置)public static final String NOTIFY_URL = "xxxxxxx/appPay/callBack";// 支付方式,取值如下:JSAPI,NATIVE,APPpublic static final String TRADE_TYPE = "APP";// 微信支付 - 统一下单地址public static final String PLACEANORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";}


 

在下辛苦填坑,转载请注明出处! 有问题欢迎指出请教加VX kuaibaojing

这篇关于IONIC3微信支付 (附后台JAVA)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/212284

相关文章

springboot健康检查监控全过程

《springboot健康检查监控全过程》文章介绍了SpringBoot如何使用Actuator和Micrometer进行健康检查和监控,通过配置和自定义健康指示器,开发者可以实时监控应用组件的状态,... 目录1. 引言重要性2. 配置Spring Boot ActuatorSpring Boot Act

使用Java解析JSON数据并提取特定字段的实现步骤(以提取mailNo为例)

《使用Java解析JSON数据并提取特定字段的实现步骤(以提取mailNo为例)》在现代软件开发中,处理JSON数据是一项非常常见的任务,无论是从API接口获取数据,还是将数据存储为JSON格式,解析... 目录1. 背景介绍1.1 jsON简介1.2 实际案例2. 准备工作2.1 环境搭建2.1.1 添加

Java实现任务管理器性能网络监控数据的方法详解

《Java实现任务管理器性能网络监控数据的方法详解》在现代操作系统中,任务管理器是一个非常重要的工具,用于监控和管理计算机的运行状态,包括CPU使用率、内存占用等,对于开发者和系统管理员来说,了解这些... 目录引言一、背景知识二、准备工作1. Maven依赖2. Gradle依赖三、代码实现四、代码详解五

java如何分布式锁实现和选型

《java如何分布式锁实现和选型》文章介绍了分布式锁的重要性以及在分布式系统中常见的问题和需求,它详细阐述了如何使用分布式锁来确保数据的一致性和系统的高可用性,文章还提供了基于数据库、Redis和Zo... 目录引言:分布式锁的重要性与分布式系统中的常见问题和需求分布式锁的重要性分布式系统中常见的问题和需求

SpringBoot基于MyBatis-Plus实现Lambda Query查询的示例代码

《SpringBoot基于MyBatis-Plus实现LambdaQuery查询的示例代码》MyBatis-Plus是MyBatis的增强工具,简化了数据库操作,并提高了开发效率,它提供了多种查询方... 目录引言基础环境配置依赖配置(Maven)application.yml 配置表结构设计demo_st

在Ubuntu上部署SpringBoot应用的操作步骤

《在Ubuntu上部署SpringBoot应用的操作步骤》随着云计算和容器化技术的普及,Linux服务器已成为部署Web应用程序的主流平台之一,Java作为一种跨平台的编程语言,具有广泛的应用场景,本... 目录一、部署准备二、安装 Java 环境1. 安装 JDK2. 验证 Java 安装三、安装 mys

Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单

《Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单》:本文主要介绍Springboot的ThreadPoolTaskScheduler线... 目录ThreadPoolTaskScheduler线程池实现15分钟不操作自动取消订单概要1,创建订单后

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

SpringCloud集成AlloyDB的示例代码

《SpringCloud集成AlloyDB的示例代码》AlloyDB是GoogleCloud提供的一种高度可扩展、强性能的关系型数据库服务,它兼容PostgreSQL,并提供了更快的查询性能... 目录1.AlloyDBjavascript是什么?AlloyDB 的工作原理2.搭建测试环境3.代码工程1.

Java调用Python代码的几种方法小结

《Java调用Python代码的几种方法小结》Python语言有丰富的系统管理、数据处理、统计类软件包,因此从java应用中调用Python代码的需求很常见、实用,本文介绍几种方法从java调用Pyt... 目录引言Java core使用ProcessBuilder使用Java脚本引擎总结引言python