1. 概述
Stripe 是一家基於雲的服務,使企業和個人能夠通過互聯網接收付款,並提供客户端庫(JavaScript 和原生移動)和服務器端庫(Java、Ruby、Node.js 等)。
Stripe 提供了一層抽象,從而降低了接收付款的複雜性。因此,我們無需直接處理信用卡詳情——而是處理一個代表授權收費的代幣。
在本教程中,我們將創建一個示例 Spring Boot 項目,允許用户輸入信用卡,然後使用 Stripe API for Java 為該卡片收費。
2. 依賴項
為了在項目中利用 Stripe API for Java,我們在 pom.xml 中添加相應的依賴項:
<dependency>
<groupId>com.stripe</groupId>
<artifactId>stripe-java</artifactId>
<version>4.2.0</version>
</dependency>
我們可以在其最新版本在 Maven Central 倉庫中找到它:https://mvnrepository.com/artifact/com.stripe/stripe-java。
對於我們的示例項目,我們將利用 spring-boot-starter-parent:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.2</version>
</parent>我們還將使用 Lombok 減少樣板代碼,Thymeleaf 將作為模板引擎,用於提供動態網頁。
由於我們使用 spring-boot-starter-parent 來管理這些庫的版本,因此我們無需在 pom.xml 中包含它們的版本。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>請注意,如果您使用的是 NetBeans,則可能需要明確地使用 Lombok 版本 1.16.16,因為 Spring Boot 1.5.2 中提供的 Lombok 版本存在一個 bug,導致 NetBeans 生成大量錯誤。
3. API 密鑰
在與 Stripe 溝通並執行信用卡交易之前,我們需要註冊一個 Stripe 賬户並獲取秘密/公共 Stripe API 密鑰。
確認賬户後,我們將登錄訪問 Stripe 控制面板。然後,在左側菜單中選擇“API 密鑰”:
將會有一對秘密/公共密鑰,用於測試和生產環境:一組用於測試和一組用於生產環境。請保持此選項卡打開,以便我們稍後使用這些密鑰。
4. 總體流程
信用卡收費將分為五個簡單的步驟,涉及前端(在瀏覽器中運行)、後端(我們的 Spring Boot 應用程序)和 Stripe。
- 用户訪問結算頁面並點擊“使用卡支付”。
- 用户將看到 Stripe 結算覆蓋對話框,其中填寫信用卡詳細信息。
- 用户通過點擊“支付
” 確認,這將: - 將信用卡發送到 Stripe
- 在響應中獲取一個令牌,該令牌將附加到現有的表單
- 使用金額、公共 API 密鑰、電子郵件和令牌提交該表單到我們的後端
- 我們的後端通過令牌、金額和密鑰與 Stripe 聯繫。
- 後端檢查 Stripe 的響應並向用户提供操作反饋。
我們在後續部分將更詳細地介紹每個步驟。
5. 訂單表單
Stripe Checkout 是一個 可定製、適用於移動設備且可本地化的小部件,用於渲染一個表單,以輸入信用卡詳細信息。 通過包含和配置 “checkout.js”,它負責:
- “使用卡片”按鈕渲染
- 支付覆蓋層對話渲染(在點擊“使用卡片”後觸發)
- 信用卡驗證
- “記住我”功能(將卡片與移動號碼關聯)
- 將信用卡發送到 Stripe 並用令牌替換表單中的相應值(在點擊“支付
”後觸發)
如果我們需要對訂單表單進行比 Stripe Checkout 提供的更多控制,則可以使用 Stripe Elements。
接下來,我們將分析準備表單的控制器,然後是表單本身。
5.1. 控制器
讓我們首先創建一個控制器,用於準備模型,並提供結賬表單所需的所有必要信息。
首先,我們需要從 Stripe 控制面板複製我們測試版本的公鑰,並將其用於定義 STRIPE_PUBLIC_KEY 作為環境變量。然後,我們使用此值在 stripePublicKey 字段中。
我們還在這裏手動設置 currency (貨幣) 和 amount (金額,以分表示),僅用於演示目的,但在實際應用中,我們可能會設置產品/銷售 ID,以便從中檢索實際值。
然後,我們將 dispatch 到結賬視圖,該視圖包含結賬表單:
@Controller
public class CheckoutController {
@Value("${STRIPE_PUBLIC_KEY}")
private String stripePublicKey;
@RequestMapping("/checkout")
public String checkout(Model model) {
model.addAttribute("amount", 50 * 100); // in cents
model.addAttribute("stripePublicKey", stripePublicKey);
model.addAttribute("currency", ChargeRequest.Currency.EUR);
return "checkout";
}
}關於 Stripe API 密鑰,您可以為每個應用程序(測試環境 vs. 生產環境)定義它們為環境變量。
與密碼或任何敏感信息一樣,最好將密鑰保存在版本控制系統之外。
5.2. 表單
“使用卡片支付”按鈕和結算對話框是通過添加包含腳本的表單來包含的,該表單已正確配置,包括使用數據屬性。
<form action='/charge' method='POST' id='checkout-form'>
<input type='hidden' th:value='${amount}' name='amount' />
<label>Price:<span th:text='${amount/100}' /></label>
<!-- NOTE: data-key/data-amount/data-currency will be rendered by Thymeleaf -->
<script
src='https://checkout.stripe.com/checkout.js'
class='stripe-button'
th:attr='data-key=${stripePublicKey},
data-amount=${amount},
data-currency=${currency}'
data-name='Baeldung'
data-description='Spring course checkout'
data-image
='https://www.baeldung.com/wp-content/themes/baeldung/favicon/android-chrome-192x192.png'
data-locale='auto'
data-zip-code='false'>
</script>
</form>“checkout.js”腳本在提交之前自動向 Stripe 發起請求,然後將 Stripe 令牌和 Stripe 用户電子郵件作為隱藏字段“stripeToken”和“stripeEmail”附加。
這些字段將與其它表單字段一起提交到我們的後端。腳本的數據屬性不會提交。
我們使用 Thymeleaf 來渲染屬性“data-key”、“data-amount”和“data-currency”。
金額 (“data-amount“) 僅用於顯示目的(與“data-currency”一起)。其單位是所用貨幣的分,因此我們將它除以 100 以顯示。
Stripe 公鑰在用户請求付款後傳遞給 Stripe。請勿在此處使用密鑰,因為該密鑰發送到瀏覽器。
6. 計費操作
對於服務器端處理,我們需要定義用於結算表單使用的 POST 請求處理程序。下面我們來看一下用於計費操作所需的類。
6.1. ChargeRequest 實體
讓我們定義 ChargeRequest POJO,它將作為充電操作中的業務實體:
@Data
public class ChargeRequest {
public enum Currency {
EUR, USD;
}
private String description;
private int amount;
private Currency currency;
private String stripeEmail;
private String stripeToken;
}6.2. 服務
讓我們編寫一個 StripeService 類,用於 向 Stripe 實際計費操作傳遞信息:
@Service
public class StripeService {
@Value("${STRIPE_SECRET_KEY}")
private String secretKey;
@PostConstruct
public void init() {
Stripe.apiKey = secretKey;
}
public Charge charge(ChargeRequest chargeRequest)
throws AuthenticationException, InvalidRequestException,
APIConnectionException, CardException, APIException {
Map<String, Object> chargeParams = new HashMap<>();
chargeParams.put("amount", chargeRequest.getAmount());
chargeParams.put("currency", chargeRequest.getCurrency());
chargeParams.put("description", chargeRequest.getDescription());
chargeParams.put("source", chargeRequest.getStripeToken());
return Charge.create(chargeParams);
}
}如 CheckoutController 所示,secretKey 字段是從我們從 Stripe 儀表盤複製的 STRIPE_SECRET_KEY 環境變量中填充的。
在服務初始化後,此密鑰將用於所有後續的 Stripe 操作。
Stripe 庫返回的對象代表 支付操作,幷包含有用的數據,例如操作 ID。
6.3. 控制器
最後,讓我們編寫控制器,該控制器將接收由結算表單發出的 POST 請求,並通過我們的 StripeService 將支付提交到 Stripe。
請注意,“ChargeRequest” 參數會自動初始化為包含表單中 “amount”、“stripeEmail” 和 “stripeToken” 請求參數。
@Controller
public class ChargeController {
@Autowired
private StripeService paymentsService;
@PostMapping("/charge")
public String charge(ChargeRequest chargeRequest, Model model)
throws StripeException {
chargeRequest.setDescription("Example charge");
chargeRequest.setCurrency(Currency.EUR);
Charge charge = paymentsService.charge(chargeRequest);
model.addAttribute("id", charge.getId());
model.addAttribute("status", charge.getStatus());
model.addAttribute("chargeId", charge.getId());
model.addAttribute("balance_transaction", charge.getBalanceTransaction());
return "result";
}
@ExceptionHandler(StripeException.class)
public String handleError(Model model, StripeException ex) {
model.addAttribute("error", ex.getMessage());
return "result";
}
}在成功執行後,我們會在模型中添加狀態、操作 ID、充值 ID 和餘額交易 ID,以便稍後向用户展示(第 7 節)。 這用於説明 充值對象 的一些內容。
我們的 ExceptionHandler 將處理在充值操作期間拋出的類型為 StripeException 的異常。
如果需要更精細的錯誤處理,我們可以為 StripeException 的子類(例如 CardException、RateLimitException 或 AuthenticationException)添加單獨的處理程序。
“result” 視圖渲染充值操作的結果。
7. 顯示結果
用於顯示結果的 HTML 是一個基本的 Thymeleaf 模板,它顯示了充電操作的結果。用户通過 ChargeController 無論充電操作是否成功,都會被引導到這裏:
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml' xmlns:th='http://www.thymeleaf.org'>
<head>
<title>Result</title>
</head>
<body>
<h3 th:if='${error}' th:text='${error}' style='color: red;'></h3>
<div th:unless='${error}'>
<h3 style='color: green;'>Success!</h3>
<div>Id.: <span th:text='${id}' /></div>
<div>Status: <span th:text='${status}' /></div>
<div>Charge id.: <span th:text='${chargeId}' /></div>
<div>Balance transaction id.: <span th:text='${balance_transaction}' /></div>
</div>
<a href='/checkout.html'>Checkout again</a>
</body>
</html>成功時,用户將看到充電操作的詳細信息:
出錯時,用户將看到 Stripe 返回的錯誤消息:
8. 結論
在本教程中,我們演示瞭如何利用 Stripe Java API 向信用卡中收取費用。在未來,我們可以重用我們的服務端代碼來為原生移動應用程序提供服務。為了測試整個支付流程,我們不需要使用真實的信用卡(即使在測試模式下)。我們可以依賴於 Stripe 測試卡 代替。
支付操作是 Stripe Java API 中眾多可能性之一。 官方 API 參考 將引導我們完成所有操作。