微信APP支付-Android+springboot搭建后端(一)
这篇教程将详细的介绍如何实现微信APP支付,分为Android移动端开发和springboot后端开发,有一些在开发过程中遇到的坑将会被标注,解决方案也会给出。
一、准备工作
准备工作就是获取必要的参数,注册微信商户平台和微信开放平台分别获取到商户号和APPID,并且在微信商户平台申请API证书、设置API密钥、设置APIv3密钥等
这些工作在公司里会有相关人员做好,将参数给出,直接拿来用即可。准备工作就是比较繁琐,而且微信开发者认证需要300元。做完相关工作后,一定要查看权限是否申请到,微信商户平台是否关联APPID等。由于这些东西全是我一个人做,所以对流程比较了解。
二、Springboot后端开发
先介绍一下微信支付的后端开发,微信APP支付开发与支付宝支付不一样,所以在这边需要将后端搭建好。打开微信支付的文档中心,我们主要根据官方给的提示按照步骤操作就可以了。这里我们就仅展示APP下单。
1、创建项目
主要的目录结构如下。包含了配置类,工具类等。包名 com.atguigu.paymentdemo(借鉴了网课)
2、配置文件application.yml和wxpay.properties
application.yml
server:port: 8090 #服务端口spring:application:name: payment-demo #应用的名字jackson:date-format: yyyy-MM-dd HH:mm:sstime-zone: GMT+8datasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://ip:3310/payment_demo?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=falseusername: 用户名password: 密码mybatis-plus:configuration: #sql日志log-impl: org.apache.ibatis.logging.stdout.StdOutImplmapper-locations: classpath:com/atguigu/paymentdemo/mapper/xml/*.xmllogging:level:root: info
wxpay.properties
# 微信支付相关参数
# 商户号
wxpay.mch-id=商户号写自己的
# 商户API证书序列号
wxpay.mch-serial-no=写自己的# 商户私钥文件
wxpay.private-key-path=apiclient_key.pem
# APIv3密钥
wxpay.api-v3-key=写自己的
# API密钥
wxpay.api-key=写自己的
# APPID
wxpay.appid=写自己的
# 微信服务器地址
wxpay.domain=https://api.mch.weixin.qq.com
# 接收结果通知地址
wxpay.notify-domain=https://ip或者域名
注意:
- 这里需要注意将上面的参数改为自己申请到的数据(商户号、序列号、秘钥等等),逐个修改就可以
- 这里将申请到的商户私钥文件apiclient_key.pem放在了根目录下
- 接受回调的通知地址wxpay.notify-domain这里需要注意一下,官方文档说必须为https地址,所以这里我们就使用一个内网穿透工具生成一个HTTPS地址。请自行查看ngrok这个工具。公司里应该都会给的。
3、配置文件pom.xml
pom.xml
<!--Swagger--><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.7.0</version></dependency><!--Swagger ui--><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>2.7.0</version></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--mysql 驱动--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency><!--MyBatis-Plus:是MyBatis的增强--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.3.1</version></dependency><!-- 代码生成器配置 --><dependency><groupId>org.apache.velocity</groupId><artifactId>velocity-engine-core</artifactId><version>2.0</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.4.1</version></dependency><!-- 生成自定义配置的元数据信息 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><!--微信支付SDK--><dependency><groupId>com.github.wechatpay-apiv3</groupId><artifactId>wechatpay-apache-httpclient</artifactId><version>0.3.0</version></dependency><!--json处理器--><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId></dependency><!--网络请求--><!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp --><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>4.9.3</version></dependency>
由于项目打包时会将mapper目录下xml文件漏掉,所以我们在application.yml文件下配置了classpath:com/atguigu/paymentdemo/mapper/xml/.xml,同时在pom文件下的build下加入下面代码就可以了,这样就会在打包时将java目录中的.xml文件也进行打包。
<build><!-- 项目打包时会将java目录中的*.xml文件也进行打包 --><resources><resource><directory>src/main/java</directory><includes><include>**/*.xml</include></includes><filtering>false</filtering></resource></resources></build>
为了让读者更好的复现这些功能,下面尽可能的将用到的代码都放在文章里。
4、vo包
这个目录主要是生成两个文件,使用swagger测试时查看响应码以及收到消息。
ResultCode
public interface ResultCode {public static Integer SUCCESS = 20000;//成功public static Integer ERROR = 20001;//失败
}
R
@Data
@Accessors(chain = true)
public class R {@ApiModelProperty(value = "是否成功")private Boolean success;@ApiModelProperty(value = "返回码")private Integer code;@ApiModelProperty(value = "返回消息")private String message;@ApiModelProperty(value = "返回数据")private Map<String, Object> data = new HashMap<String, Object>();//构造方法私有化private R(){}//链式编程//成功静态方法public static R ok(){R r = new R();r.setSuccess(true);r.setCode(ResultCode.SUCCESS);r.setMessage("成功");return r;}//失败静态方法public static R error(){R r = new R();r.setSuccess(false);r.setCode(ResultCode.ERROR);r.setMessage("失败");return r;}public R success(Boolean success){this.setSuccess(success);return this;}public R message(String message){this.setMessage(message);return this;}public R code(Integer code){this.setCode(code);return this;}public R data(String key, Object value){this.data.put(key, value);return this;}public R data(Map<String, Object> map){this.setData(map);return this;}
}
5、config包
这个目录主要是配置信息,配置Swagger、MyBatisPlus以及WxPay微信支付的参数
Swagger2Config
@Configuration
@EnableSwagger2
public class Swagger2Config {@Beanpublic Docket docket(){return new Docket(DocumentationType.SWAGGER_2).apiInfo(new ApiInfoBuilder().title("微信支付案例接口文档").build());}
}
MyBatisPlusConfig
@Configuration
@MapperScan("com.atguigu.paymentdemo.mapper")
@EnableTransactionManagement //启用事务管理
public class MyBatisPlusConfig {}
WxPayConfig
这个文件就是读取到wxpay.properties的信息
@Configuration
@PropertySource("classpath:wxpay.properties") //读取配置文件
@ConfigurationProperties(prefix="wxpay") //读取wxpay节点
@Data //使用set方法将wxpay节点中的值填充到当前类的属性中
@Slf4j
public class WxPayConfig {// 商户号private String mchId;// 商户API证书序列号private String mchSerialNo;// 商户私钥文件private String privateKeyPath;// APIv3密钥private String apiV3Key;// API密钥private String apiKey;// APPIDprivate String appid;// 微信服务器地址private String domain;// 接收结果通知地址private String notifyDomain;/*** 获取商户的私钥文件* @param filename* @return*/public PrivateKey getPrivateKey(String filename){try {return PemUtil.loadPrivateKey(new FileInputStream(filename));} catch (FileNotFoundException e) {throw new RuntimeException("私钥文件不存在", e);}}/*** 获取签名验证器* @return*/@Beanpublic ScheduledUpdateCertificatesVerifier getVerifier(){log.info("获取签名验证器");//获取商户私钥PrivateKey privateKey = getPrivateKey(privateKeyPath);//私钥签名对象PrivateKeySigner privateKeySigner = new PrivateKeySigner(mchSerialNo, privateKey);//身份认证对象WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner);// 使用定时更新的签名验证器,不需要传入证书ScheduledUpdateCertificatesVerifier verifier = new ScheduledUpdateCertificatesVerifier(wechatPay2Credentials,apiV3Key.getBytes(StandardCharsets.UTF_8));return verifier;}/*** 获取http请求对象* @param verifier* @return*/@Bean(name = "wxPayClient")public CloseableHttpClient getWxPayClient(ScheduledUpdateCertificatesVerifier verifier){log.info("获取httpClient");//获取商户私钥PrivateKey privateKey = getPrivateKey(privateKeyPath);WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create().withMerchant(mchId, mchSerialNo, privateKey).withValidator(new WechatPay2Validator(verifier));// ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClient// 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新CloseableHttpClient httpClient = builder.build();return httpClient;}/*** 获取HttpClient,无需进行应答签名验证,跳过验签的流程*/@Bean(name = "wxPayNoSignClient")public CloseableHttpClient getWxPayNoSignClient(){//获取商户私钥PrivateKey privateKey = getPrivateKey(privateKeyPath);//用于构造HttpClientWechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()//设置商户信息.withMerchant(mchId, mchSerialNo, privateKey)//无需进行签名验证、通过withValidator((response) -> true)实现.withValidator((response) -> true);// 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新CloseableHttpClient httpClient = builder.build();log.info("== getWxPayNoSignClient END ==");return httpClient;}
}
6、enums包
这个主要是定义微信支付的提供的地址,将其设置为枚举型,进行拼接就可以组装成URL地址
WxApiType
@AllArgsConstructor
@Getter
public enum WxApiType {/*** APP下单*/APP_PAY("/v3/pay/transactions/app"),/*** 类型*/private final String type;
}
WxNotifyType
@AllArgsConstructor
@Getter
public enum WxNotifyType {/*** APP支付通知*/APP_NOTIFY("/api/wx-pay/app/notify"),/*** 类型*/private final String type;
}
OrderStatus
@AllArgsConstructor
@Getter
public enum OrderStatus {/*** 未支付*/NOTPAY("未支付"),/*** 支付成功*/SUCCESS("支付成功"),/*** 已关闭*/CLOSED("超时已关闭"),/*** 已取消*/CANCEL("用户已取消"),/*** 退款中*/REFUND_PROCESSING("退款中"),/*** 已退款*/REFUND_SUCCESS("已退款"),/*** 退款异常*/REFUND_ABNORMAL("退款异常");/*** 类型*/private final String type;
}
PayType
@AllArgsConstructor
@Getter
public enum PayType {/*** 微信*/WXPAY("微信"),/*** 支付宝*/ALIPAY("支付宝");/*** 类型*/private final String type;
}
7、util工具包
HttpClientUtils
/*** http请求客户端*/
public class HttpClientUtils {private String url;private Map<String, String> param;private int statusCode;private String content;private String xmlParam;private boolean isHttps;public boolean isHttps() {return isHttps;}public void setHttps(boolean isHttps) {this.isHttps = isHttps;}public String getXmlParam() {return xmlParam;}public void setXmlParam(String xmlParam) {this.xmlParam = xmlParam;}public HttpClientUtils(String url, Map<String, String> param) {this.url = url;this.param = param;}public HttpClientUtils(String url) {this.url = url;}public void setParameter(Map<String, String> map) {param = map;}public void addParameter(String key, String value) {if (param == null)param = new HashMap<String, String>();param.put(key, value);}public void post() throws ClientProtocolException, IOException {HttpPost http = new HttpPost(url);setEntity(http);execute(http);}public void put() throws ClientProtocolException, IOException {HttpPut http = new HttpPut(url);setEntity(http);execute(http);}public void get() throws ClientProtocolException, IOException {if (param != null) {StringBuilder url = new StringBuilder(this.url);boolean isFirst = true;for (String key : param.keySet()) {if (isFirst) {url.append("?");isFirst = false;}else {url.append("&");}url.append(key).append("=").append(param.get(key));}this.url = url.toString();}HttpGet http = new HttpGet(url);execute(http);}/*** set http post,put param*/private void setEntity(HttpEntityEnclosingRequestBase http) {if (param != null) {List<NameValuePair> nvps = new LinkedList<NameValuePair>();for (String key : param.keySet())nvps.add(new BasicNameValuePair(key, param.get(key))); // 参数http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 设置参数}if (xmlParam != null) {http.setEntity(new StringEntity(xmlParam, Consts.UTF_8));}}private void execute(HttpUriRequest http) throws ClientProtocolException,IOException {CloseableHttpClient httpClient = null;try {if (isHttps) {SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {// 信任所有public boolean isTrusted(X509Certificate[] chain,String authType)throws CertificateException {return true;}}).build();SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();} else {httpClient = HttpClients.createDefault();}CloseableHttpResponse response = httpClient.execute(http);try {if (response != null) {if (response.getStatusLine() != null)statusCode = response.getStatusLine().getStatusCode();HttpEntity entity = response.getEntity();// 响应内容content = EntityUtils.toString(entity, Consts.UTF_8);}} finally {response.close();}} catch (Exception e) {e.printStackTrace();} finally {httpClient.close();}}public int getStatusCode() {return statusCode;}public String getContent() throws ParseException, IOException {return content;}}
HttpUtils
public class HttpUtils {/*** 将通知参数转化为字符串* @param request* @return*/public static String readData(HttpServletRequest request) {BufferedReader br = null;try {StringBuilder result = new StringBuilder();br = request.getReader();for (String line; (line = br.readLine()) != null; ) {if (result.length() > 0) {result.append("\n");}result.append(line);}return result.toString();} catch (IOException e) {throw new RuntimeException(e);} finally {if (br != null) {try {br.close();} catch (IOException e) {e.printStackTrace();}}}}
}
OrderNoUtils
/*** 订单号工具类** @author qy* @since 1.0*/
public class OrderNoUtils {/*** 获取订单编号* @return*/public static String getOrderNo() {return "ORDER_" + getNo();}/*** 获取退款单编号* @return*/public static String getRefundNo() {return "REFUND_" + getNo();}/*** 获取编号* @return*/public static String getNo() {SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");String newDate = sdf.format(new Date());String result = "";Random random = new Random();for (int i = 0; i < 3; i++) {result += random.nextInt(10);}return newDate + result;}}
WechatPay2ValidatorForRequest
/*** @author xy-peng*/
public class WechatPay2ValidatorForRequest {protected static final Logger log = LoggerFactory.getLogger(WechatPay2ValidatorForRequest.class);/*** 应答超时时间,单位为分钟*/protected static final long RESPONSE_EXPIRED_MINUTES = 5;protected final Verifier verifier;protected final String requestId;protected final String body;public WechatPay2ValidatorForRequest(Verifier verifier, String requestId, String body) {this.verifier = verifier;this.requestId = requestId;this.body = body;}protected static IllegalArgumentException parameterError(String message, Object... args) {message = String.format(message, args);return new IllegalArgumentException("parameter error: " + message);}protected static IllegalArgumentException verifyFail(String message, Object... args) {message = String.format(message, args);return new IllegalArgumentException("signature verify fail: " + message);}public final boolean validate(HttpServletRequest request) throws IOException {try {//处理请求参数validateParameters(request);//构造验签名串String message = buildMessage(request);String serial = request.getHeader(WECHAT_PAY_SERIAL);String signature = request.getHeader(WECHAT_PAY_SIGNATURE);//验签if (!verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature)) {throw verifyFail("serial=[%s] message=[%s] sign=[%s], request-id=[%s]",serial, message, signature, requestId);}} catch (IllegalArgumentException e) {log.warn(e.getMessage());return false;}return true;}protected final void validateParameters(HttpServletRequest request) {// NOTE: ensure HEADER_WECHAT_PAY_TIMESTAMP at lastString[] headers = {WECHAT_PAY_SERIAL, WECHAT_PAY_SIGNATURE, WECHAT_PAY_NONCE, WECHAT_PAY_TIMESTAMP};String header = null;for (String headerName : headers) {header = request.getHeader(headerName);if (header == null) {throw parameterError("empty [%s], request-id=[%s]", headerName, requestId);}}//判断请求是否过期String timestampStr = header;try {Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestampStr));// 拒绝过期请求if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= RESPONSE_EXPIRED_MINUTES) {throw parameterError("timestamp=[%s] expires, request-id=[%s]", timestampStr, requestId);}} catch (DateTimeException | NumberFormatException e) {throw parameterError("invalid timestamp=[%s], request-id=[%s]", timestampStr, requestId);}}protected final String buildMessage(HttpServletRequest request) throws IOException {String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);String nonce = request.getHeader(WECHAT_PAY_NONCE);return timestamp + "\n"+ nonce + "\n"+ body + "\n";}protected final String getResponseBody(CloseableHttpResponse response) throws IOException {HttpEntity entity = response.getEntity();return (entity != null && entity.isRepeatable()) ? EntityUtils.toString(entity) : "";}}
这上面都是网课里的东西,我直接拿过来用的,都复制过来了。
8、controller包
上面的都是相关的配置文件,终于到重点部分了。在这里我省略了一些东西,Android端发起微信支付的时候,没有给参数,只展示了这个场景,每个人的需求不一样,如果需要参数,请自行改动接口,下面代码我给注释掉了。
WxPayController
@RestController
@RequestMapping("/api/wx-pay")
@Api(tags = "微信APP支付APIv3")
@Slf4j
public class WxPayController {@Resourceprivate WxPayService wxPayService;/*** APP下单* @param* @return* @throws Exception*/@ApiOperation("调用统一下单API,生成APP下单的预支付交易会话标识")
// @PostMapping("/native/{productId}")@PostMapping("/native")
// public R APPPay(@PathVariable Long productId) throws Exception {public R APPPay() throws Exception {log.info("发起支付请求 v3");//返回支付所需要的参数给Android端Map<String, Object> map = wxPayService.appPay();log.info("map====>{}",map);return R.ok().setData(map);}
}
9、service包
WxPayService
public interface WxPayService {/*** app下单* @param* @return*/Map<String, Object> appPay() throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException;
}
WxPayServiceImpl
@Service
@Slf4j
public class WxPayServiceImpl implements WxPayService {@Resourceprivate WxPayConfig wxPayConfig;@Resourceprivate CloseableHttpClient wxPayClient;protected static final SecureRandom RANDOM = new SecureRandom();/*** APP下单* @param* @return*/@Overridepublic Map<String, Object> appPay() throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {log.info("生成订单");String orderNo = OrderNoUtils.getOrderNo();//生成订单log.info("调用统一下单API");//调用统一下单APIHttpPost httpPost = new HttpPost(wxPayConfig.getDomain().concat(WxApiType.APP_PAY.getType()));// 请求body参数Gson gson = new Gson();Map paramsMap = new HashMap();paramsMap.put("appid", wxPayConfig.getAppid());paramsMap.put("mchid", wxPayConfig.getMchId());paramsMap.put("description", "whq烤肉");paramsMap.put("out_trade_no", orderNo);paramsMap.put("notify_url", wxPayConfig.getNotifyDomain().concat(WxNotifyType.APP_NOTIFY.getType()));Map amountMap = new HashMap();amountMap.put("total", 1);amountMap.put("currency", "CNY");paramsMap.put("amount", amountMap);//将参数转换成json字符串String jsonParams = gson.toJson(paramsMap);log.info("请求参数 ===> {}" + jsonParams);StringEntity entity = new StringEntity(jsonParams,"utf-8");entity.setContentType("application/json");httpPost.setEntity(entity);httpPost.setHeader("Accept", "application/json");//完成签名并执行请求CloseableHttpResponse response = wxPayClient.execute(httpPost);try {String bodyAsString = EntityUtils.toString(response.getEntity());//响应体int statusCode = response.getStatusLine().getStatusCode();//响应状态码if (statusCode == 200) { //处理成功log.info("成功, 返回结果 = " + bodyAsString);} else if (statusCode == 204) { //处理成功,无返回Bodylog.info("成功");} else {log.info("APP下单失败,响应码 = " + statusCode+ ",返回结果 = " + bodyAsString);throw new IOException("request failed");}//响应结果Map<String, String> resultMap = gson.fromJson(bodyAsString, HashMap.class);//得到返回参数String prepay_id = resultMap.get("prepay_id");Map gettoken = getToken(wxPayConfig.getAppid(),prepay_id);String nonceStr = (String) gettoken.get("nonceStr");String sign = (String) gettoken.get("signature");long timestamp = (long) gettoken.get("timestamp");//返回得到的返回参数Map<String, Object> map = new HashMap<>();map.put("prepayid", prepay_id);map.put("sign", sign);map.put("appid", wxPayConfig.getAppid());map.put("partnerid", wxPayConfig.getMchId());map.put("packagevalue", "Sign=WXPay");map.put("noncestr", nonceStr);map.put("timestamp", timestamp);return map;} finally {response.close();}}/*** 生成字符串* @return*/protected String generateNonceStr() {char[] nonceChars = new char[32];for(int index = 0; index < nonceChars.length; ++index) {nonceChars[index] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".charAt(RANDOM.nextInt("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".length()));}return new String(nonceChars);}/*** 生成签名值* @param appid* @param prepay_id* @return* @throws IOException* @throws SignatureException* @throws NoSuchAlgorithmException* @throws InvalidKeyException*/Map<String,Object> getToken(String appid,String prepay_id) throws IOException, SignatureException, NoSuchAlgorithmException, InvalidKeyException {//随机字符串String nonceStr = this.generateNonceStr();//随机字符串//时间戳long timestamp = System.currentTimeMillis() / 1000;//从下往上依次生成String message = buildMessage(appid, timestamp, nonceStr, prepay_id);//签名String signature = sign(message.getBytes("utf-8"));Map<String , Object> map = new HashMap<>();map.put("timestamp",timestamp);map.put("nonceStr",nonceStr);map.put("signature",signature);return map;}String sign(byte[] message) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException {//签名方式Signature sign = Signature.getInstance("SHA256withRSA");//私钥,通过MyPrivateKey来获取,这是个静态类可以接调用方法 ,需要的是_key.pem文件的绝对路径配上文件名//获取商户私钥PrivateKey privateKey = wxPayConfig.getPrivateKey(wxPayConfig.getPrivateKeyPath());sign.initSign(privateKey);sign.update(message);return Base64.getEncoder().encodeToString(sign.sign());}/*** 按照前端签名文档规范进行排序,\n是换行* @param appid* @param timestamp* @param nonceStr* @param prepay_id* @return*/String buildMessage(String appid, long timestamp,String nonceStr,String prepay_id) {return appid + "\n"+ timestamp + "\n"+ nonceStr + "\n"+ prepay_id + "\n";}
}
到这里后端的项目就算完成了,此时运行起来就行了。打开swagger进行测试
返回参数就是这些。我们Android进行接收就完成了!
注意:
以上只是简单的给出微信APP下单的实现,公司里的需要要更完善,比如保存订单和订单状态,产品等等,这些都要与数据库交互,所以在上面基础上继续完成功能就可以了。
微信APP支付-Android+springboot搭建后端(一)相关推荐
- 实现微信app支付的springboot项目
1.1请先完成微信APP支付接入商户服务中心 1.2详情请参考微信官方文档:https://open.weixin.qq.com/ 2.application.yml文件的配置如下 #微信支付配置 t ...
- android 支付系统 demo,微信APP支付Android Demo详解
根据这篇文章的步骤,可以让你的账号跑通android app的微信支付. 前提:已经有开通微信支付,有商户号,密钥. 点击图片可以打开大图查看. 1. 下载Android app Demo 2. 安装 ...
- Android安卓原生接入微信app支付PHP服务端
Android安卓接入微信app支付PHP服务端 1.进入微信商户平台查看统一下单接口文档. 在查看完统一下单文档后,能够看到需要传递给微信"统一下单接口"地址的参数有哪些 统一下 ...
- android微信支付都需要什么意思,Android开发微信APP支付功能的要点小结
基本概念 包名值得是你APP的包,在创建工程时候设置的,需要在微信支付平台上面设置. 签名指的是你生成APK时候所用的签名文件的md5,去掉:全部小写,需要在微信支付平台上面设置. 调试阶段,签名文件 ...
- Android版-微信APP支付
首发地址: Android版-微信APP支付 欢迎留言.转发 微信极速开发系列文章(微信支付.授权获取用户信息等):点击这里 目录 1.注册账号.开发者认证 2.添加应用 3.申请微信支付 4.技术开 ...
- 微信app支付 java后台接Android
抽时间整理一下之前项目中的微信app支付,以备以后需要,如果对你可以有点帮助是最好不过的: 直接上代码: public class WeChatAppConfig {/*** 预支付请求地址*/pub ...
- java后端+uniapp 对接微信app支付 报错-1
问题描述: 原因分析: 上面两张图片完美的呈现了对接微信APP支付所能遇见的问题 排查流程: 检测自己的app是否 以及注册到微信开放平台 并且填写了与APP中的包名相对应的 检测自己的App是否开通 ...
- 微信APP支付填坑记
首先吐槽几句,微信支付很强大,古人有诗赞曰:自古多坑空余恨,此坑绵绵无绝期. 先说几个必做前置任务: 1 在微信商户平台设置你的公用key,程序里生成sign签名时,要用到这个key.为md5 32位 ...
- 微信App支付全解析
简单介绍了微信移动支付的申请.接入.使用.确认支付结果等相关流程 0 系列文章 系列一 微信App支付全解析 系列二 支付宝App支付全解析 系列三 微信公众号支付全解析 系列四 微信扫码支付全解析 ...
- 微信app支付功能-服务端的实现-python3版
微信app支付功能-服务端的实现-python3版 一:需求说明 二:微信app支付处理流程 三:所需依赖 3.1 支付配置 四:接口开发 4.1 创建订单接口 4.2 微信异步回调接口 4.3 订单 ...
最新文章
- 图解Istio原理和实践--云平台技术栈18
- 正则表达式 字符转义
- python done()什么意思_Python done
- linux下lua开发环境安装
- ElasticSearch6.x 7.x Elasticdump 在线安装、离线安装
- 忘记MySQL密码怎么办?一招教你搞定!
- C#删除字符串最后一个字符的几种方法
- 计算机通过逻辑电路实现运算,计算机组成与体系结构数据表示与运算算法和逻辑电路实现.ppt...
- PHP程序员测试题及答案
- AR公共安全及应急指挥中的应用 | TVP思享
- ubuntu linux 教程 pdf,Ubuntu 12.04 菜鸟完全使用教程(二) PDF
- numpy下载失败解决方法
- 指数波段划分以及底部反弹行业特征统计分析
- 「模拟8.19 A嚎叫..(set) B主仆..(DFS) C征程..(DP+堆优化)」
- 从输入URL到页面加载…
- Google Nexus 5 root后显示文件系统/system只读
- Hive基础知识及底层架构
- MySQL大厂优化方案轻松应对高并发!真牛!
- Unity编写冰球对战游戏 2D版
- DeepFM原理及tensorflow代码实战
热门文章
- 如何使用计算机小学生课件,小学信息技术计算机基础ppt课件
- matlab逆滤波、维纳滤波、最小二乘滤波
- 全国2009年1月电子商务与电子政务试题
- VMware Workstation Pro v16.1.0虚拟机下载安装过程
- armv6 armv7 armv7s架构的区别
- 【数智化案例展】深农集团——守护深圳“菜篮子”,腾讯安全携手深农集团保供稳价格...
- SQL Server中查询ORACLE的数据
- linux 锐捷 自动,Linux 锐捷自动交互认证
- 从头开始vue创建项目_从头开始创建Windows 7主题包
- CocosBuilder