简单实现一个苹果支付的场景

在Spring Boot项目中集成Apple Pay,需要实现以下步骤:

  1. 配置Apple开发者账户:在Apple开发者中心创建商家ID,并生成相关的支付处理证书。

  2. 配置HTTPS :Apple Pay要求服务器必须使用HTTPS协议。您可以使用JDK自带的keytool工具生成自签名证书,或从受信任的证书颁发机构获取证书。

  3. 集成支付SDK :在Spring Boot项目中,您可以使用第三方支付集成库,如pay-spring-boot-starter,来简化支付流程的实现。

以下是一个基于Spring Boot的场景示例,展示如何集成Apple Pay并处理支付回调:

1. 配置Apple Pay相关参数

application.yml中添加Apple Pay的相关配置:

yaml 复制代码
applepay:
  merchantId: your_merchant_id
  merchantCertificatePath: path_to_your_merchant_certificate.p12
  merchantCertificatePassword: your_certificate_password
  merchantIdentifier: your_merchant_identifier

2. 创建支付服务类

创建一个服务类ApplePayService,用于处理支付请求和验证支付结果:

java 复制代码
@Service
public class ApplePayService {

    @Value("${applepay.merchantId}")
    private String merchantId;

    @Value("${applepay.merchantCertificatePath}")
    private String merchantCertificatePath;

    @Value("${applepay.merchantCertificatePassword}")
    private String merchantCertificatePassword;

    @Value("${applepay.merchantIdentifier}")
    private String merchantIdentifier;

    public String createPaymentSession(String validationUrl) {
        // 加载商户证书
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        try (InputStream keyInput = new FileInputStream(merchantCertificatePath)) {
            keyStore.load(keyInput, merchantCertificatePassword.toCharArray());
        }

        // 创建SSL上下文
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(keyStore, merchantCertificatePassword.toCharArray());
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(kmf.getKeyManagers(), null, null);

        // 设置HTTPS连接
        URL url = new URL(validationUrl);
        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
        connection.setSSLSocketFactory(sslContext.getSocketFactory());
        connection.setRequestMethod("POST");
        connection.setRequestProperty("Content-Type", "application/json");

        // 构建请求数据
        JSONObject requestData = new JSONObject();
        requestData.put("merchantIdentifier", merchantIdentifier);
        requestData.put("displayName", "Your Store Name");
        requestData.put("initiative", "web");
        requestData.put("initiativeContext", "yourdomain.com");

        // 发送请求
        connection.setDoOutput(true);
        try (OutputStream os = connection.getOutputStream()) {
            os.write(requestData.toString().getBytes(StandardCharsets.UTF_8));
        }

        // 读取响应
        try (InputStream is = connection.getInputStream();
             BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            return response.toString();
        }
    }

    public boolean validatePayment(String paymentData) {
    // Apple验证服务器的URL
    String url = "https://sandbox.itunes.apple.com/verifyReceipt"; // 沙盒环境
    // String url = "https://buy.itunes.apple.com/verifyReceipt"; // 生产环境

    try {
        // 构建验证请求的JSON数据
        JSONObject requestJson = new JSONObject();
        requestJson.put("receipt-data", paymentData);
        // 如果是自动续订订阅,需要添加以下字段
        // requestJson.put("password", "your_shared_secret");

        // 创建HTTP连接
        URL verifyUrl = new URL(url);
        HttpURLConnection conn = (HttpURLConnection) verifyUrl.openConnection();
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type", "application/json");
        conn.setDoOutput(true);

        // 发送请求数据
        try (OutputStream os = conn.getOutputStream()) {
            os.write(requestJson.toString().getBytes(StandardCharsets.UTF_8));
        }

        // 读取响应数据
        int responseCode = conn.getResponseCode();
        if (responseCode == 200) {
            try (InputStream is = conn.getInputStream();
                 BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
                StringBuilder response = new StringBuilder();
                String line;
                while ((line = reader.readLine()) != null) {
                    response.append(line);
                }

                // 解析响应JSON
                JSONObject responseJson = new JSONObject(response.toString());
                int status = responseJson.getInt("status");

                // 根据status字段判断支付结果
                if (status == 0) {
                    // 验证成功,支付有效
                    return true;
                } else if (status == 21007) {
                    // 收据是沙盒环境,但发送到了生产环境的验证服务器
                    // 需要重新发送到沙盒环境进行验证
                    // 这里可以递归调用validatePayment方法,使用沙盒环境的URL
                    // 注意避免递归陷阱,确保不会无限递归
                    return validatePaymentInSandbox(paymentData);
                } else {
                    // 验证失败,支付无效
                    return false;
                }
            }
        } else {
            // HTTP响应码非200,表示请求失败
            return false;
        }
      } catch (Exception e) {
        e.printStackTrace();
        return false;
     }
   }
}

3. 创建支付控制器

创建一个控制器ApplePayController,用于处理前端的支付请求和回调:

java 复制代码
@RestController
@RequestMapping("/applepay")
public class ApplePayController {

    @Autowired
    private ApplePayService applePayService;

    @PostMapping("/payment-session")
    public ResponseEntity<String> createPaymentSession(@RequestBody Map<String, String> request) {
        String validationUrl = request.get("validationUrl");
        String paymentSession = applePayService.createPaymentSession(validationUrl);
        return ResponseEntity.ok(paymentSession);
    }

    @PostMapping("/payment-result")
    public ResponseEntity<String> handlePaymentResult(@RequestBody Map<String, Object> paymentResult) {
        String paymentData = (String) paymentResult.get("paymentData");
        boolean isValid = applePayService.validatePayment(paymentData);
        if (isValid) {
            // 处理支付成功的逻辑
            return ResponseEntity.ok("Payment successful");
        } else {
            // 处理支付失败的逻辑
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Payment validation failed");
        }
    }
}

4. 前端集成Apple Pay

在前端页面中,使用Apple提供的JavaScript库来处理Apple Pay的支付流程:

html 复制代码
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function() {
    if (window.ApplePaySession && ApplePaySession.canMakePayments()) {
        var applePayButton = document.getElementById('apple-pay-button');
        applePayButton.style.display = 'block';
        applePayButton.addEventListener('click', function() {
            var paymentRequest = {
                countryCode: 'US',
                currencyCode: 'USD',
                total: {
                    label: 'Your Store Name',
                    amount: '10.00'
                },
                supportedNetworks: ['visa', 'masterCard', 'amex'],
                merchantCapabilities: ['supports3DS']
            };

            var session = new ApplePaySession(3, paymentRequest);

            session.onvalidatemerchant = function(event) {
                fetch('/applepay/payment-session', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({ validationUrl: event.validationURL })
                })
                .then(function(response) {
                    return response.json();
                })
                .then(function(merchantSession) {
                    session.completeMerchantValidation(merchantSession);
                });
            };

            session.onpaymentauthorized = function(event) {
                var paymentData = event.payment.token.paymentData;
                fetch('/applepay/payment-result', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({ paymentData: paymentData })
                })
                .then(function(response) {
                    if (response.ok) {
                        session.completePayment(ApplePaySession.STATUS_SUCCESS);
                    } else {
                        session.completePayment(ApplePaySession.STATUS_FAILURE);
                    }
                });
            };

            session.begin();
        
相关推荐
ssshooter10 分钟前
Tauri 2 iOS 开发避坑指南:文件保存、Dialog 和 Documents 目录的那些坑
前端·后端·ios
追逐时光者23 分钟前
一个基于 .NET Core + Vue3 构建的开源全栈平台 Admin 系统
后端·.net
程序员飞哥29 分钟前
90后大龄程序员失业4个月终于上岸了
后端·面试·程序员
Cyeam2 小时前
爆火的 OpenClaw,赢在生态创新
程序员·开源·openai
彭于晏Yan2 小时前
Redisson分布式锁
spring boot·redis·分布式
GetcharZp2 小时前
Git 命令行太痛苦?这款 75k Star 的神级工具,让你告别“合并冲突”恐惧症!
后端
Victor3563 小时前
MongoDB(69)如何进行增量备份?
后端
Victor3563 小时前
MongoDB(70)如何使用副本集进行备份?
后端
databook3 小时前
别让AI代码,变成明天的技术债
人工智能·程序员·ai编程
千寻girling4 小时前
面试官 : “ 说一下 Python 中的常用的 字符串和数组 的 方法有哪些 ? ”
人工智能·后端·python