在Spring Boot项目中集成Apple Pay,需要实现以下步骤:
-
配置Apple开发者账户:在Apple开发者中心创建商家ID,并生成相关的支付处理证书。
-
配置HTTPS :Apple Pay要求服务器必须使用HTTPS协议。您可以使用JDK自带的
keytool
工具生成自签名证书,或从受信任的证书颁发机构获取证书。 -
集成支付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();