实现微信支付接口调用及回调功能,以下是完整的步骤及代码实现,基于 Spring Boot 框架。
1. 微信支付开发准备
开通微信支付
- 确保已在 微信商户平台上注册并完成企业认证。
- 获取以下重要信息:
- 商户号(
mch_id
) - 商户密钥(
key
) - 微信支付分配的公众账号 ID(
appid
)
- 商户号(
- 下载商户证书(如需处理退款)。
配置 API 安全密钥
前往商户平台的【账户设置】-【API安全】中配置 API 密钥。
2. 集成依赖
在 Spring Boot 项目中添加 HTTP 客户端依赖,例如 RestTemplate
或 OkHttp
。也可用支付宝/微信专用 SDK。
在 pom.xml
中添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.11.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
3. 微信支付统一下单接口调用
核心代码实现
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.security.MessageDigest;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.UUID;
@Service
public class WeChatPayService {
@Value("${wechat.pay.appid}")
private String appId;
@Value("${wechat.pay.mch_id}")
private String mchId;
@Value("${wechat.pay.key}")
private String apiKey;
@Value("${wechat.pay.notify_url}")
private String notifyUrl;
private static final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
public String createOrder(String orderNo, String description, int totalFee, String clientIp) throws Exception {
SortedMap<String, String> params = new TreeMap<>();
params.put("appid", appId);
params.put("mch_id", mchId);
params.put("nonce_str", UUID.randomUUID().toString().replaceAll("-", ""));
params.put("body", description);
params.put("out_trade_no", orderNo);
params.put("total_fee", String.valueOf(totalFee)); // 金额,单位分
params.put("spbill_create_ip", clientIp); // 调用支付的设备 IP
params.put("notify_url", notifyUrl); // 异步回调地址
params.put("trade_type", "NATIVE"); // 交易类型,NATIVE 表示扫码支付
// 签名
String sign = generateSign(params);
params.put("sign", sign);
// 构造 XML 请求
String requestXml = mapToXml(params);
// 使用 OkHttp 发起请求
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(MediaType.parse("application/xml; charset=utf-8"), requestXml);
Request request = new Request.Builder().url(UNIFIED_ORDER_URL).post(body).build();
Response response = client.newCall(request).execute();
// 解析返回结果
String responseXml = response.body().string();
Map<String, String> result = xmlToMap(responseXml);
if ("SUCCESS".equals(result.get("return_code")) && "SUCCESS".equals(result.get("result_code"))) {
return result.get("code_url"); // 返回支付二维码链接
} else {
throw new Exception("微信支付下单失败:" + result.get("return_msg"));
}
}
private String generateSign(SortedMap<String, String> params) throws Exception {
StringBuilder sb = new StringBuilder();
params.forEach((key, value) -> {
if (value != null && !"".equals(value) && !"sign".equals(key)) {
sb.append(key).append("=").append(value).append("&");
}
});
sb.append("key=").append(apiKey);
return md5(sb.toString()).toUpperCase();
}
private String md5(String str) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(str.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte b : array) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
private String mapToXml(SortedMap<String, String> map) {
StringBuilder xml = new StringBuilder("<xml>");
map.forEach((key, value) -> xml.append("<").append(key).append(">").append(value)
.append("</").append(key).append(">"));
xml.append("</xml>");
return xml.toString();
}
private Map<String, String> xmlToMap(String xml) throws Exception {
// 使用 Jackson 或其他工具解析 XML 字符串为 Map
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(xml, Map.class);
}
}
统一下单接口调用
@RestController
@RequestMapping("/api/pay")
public class PayController {
@Autowired
private WeChatPayService weChatPayService;
@PostMapping("/createOrder")
public String createOrder(@RequestParam String orderNo,
@RequestParam String description,
@RequestParam int totalFee,
@RequestParam String clientIp) {
try {
return weChatPayService.createOrder(orderNo, description, totalFee, clientIp);
} catch (Exception e) {
e.printStackTrace();
return "Error: " + e.getMessage();
}
}
}
4. 微信支付回调处理
配置回调接口
回调地址需在统一下单时配置,示例代码如下:
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
@RestController
@RequestMapping("/api/pay")
public class PayCallbackController {
@PostMapping("/notify")
public String weChatNotify(HttpServletRequest request) {
try {
// 解析 XML 请求
String requestBody = request.getReader().lines()
.reduce("", (accumulator, actual) -> accumulator + actual);
Map<String, String> resultMap = xmlToMap(requestBody);
// 验签
String receivedSign = resultMap.get("sign");
resultMap.remove("sign");
String calculatedSign = generateSign(resultMap);
if (!calculatedSign.equals(receivedSign)) {
return "<xml><return_code>FAIL</return_code><return_msg>签名失败</return_msg></xml>";
}
// 处理业务逻辑
if ("SUCCESS".equals(resultMap.get("result_code"))) {
String orderNo = resultMap.get("out_trade_no");
// 更新订单状态
System.out.println("支付成功,订单号:" + orderNo);
}
return "<xml><return_code>SUCCESS</return_code><return_msg>OK</return_msg></xml>";
} catch (Exception e) {
e.printStackTrace();
return "<xml><return_code>FAIL</return_code><return_msg>处理失败</return_msg></xml>";
}
}
}
5. 注意事项
- 安全性:使用 HTTPS 确保传输安全。
- 验签:每次回调需要严格验签,防止伪造回调。
- 金额校验:确认支付金额与订单金额一致。
- 防止重复通知:回调接口需支持幂等性。
通过以上步骤,可以实现微信支付接口调用及回调处理功能。如果有更多需求(如退款或查询订单状态),可以扩展上述代码。
发布者:myrgd,转载请注明出处:https://www.object-c.cn/4432