1. 개요

Stripe 는 기업과 개인이 인터넷을 통해 결제를 받을 수 있도록 하는 클라우드 기반 서비스이며 클라이언트 측 라이브러리(JavaScript 및 기본 모바일)와 서버 측 라이브러리(Java, Ruby, Node.js 등)를 모두 제공합니다.

Stripe은 지불을 받는 복잡성을 줄이는 추상화 계층을 제공합니다. 결과적 으로 신용 카드 세부 정보를 직접 처리할 필요가 없습니다. 대신 청구 승인을 상징하는 토큰을 처리합니다 .

이 예제에서는 사용자가 신용 카드를 입력하고 나중에 Stripe API for Java를 사용하여 일정 금액을 카드에 청구할 수 있는 샘플 Spring Boot 프로젝트를 생성합니다 .

2. 의존성

프로젝트에서 Java용 Stripe API를 사용하기 위해 pom.xml 에 해당 의존성을 추가합니다 .

<dependency>
    <groupId>com.stripe</groupId>
    <artifactId>stripe-java</artifactId>
    <version>4.2.0</version>
</dependency>

Maven Central 저장소에서 최신 버전을 찾을 수 있습니다 .

샘플 프로젝트의 경우 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를 사용 하는 경우 Spring Boot 1.5.2와 함께 제공되는 Lombok 버전의 버그로 인해 NetBeans에서 많은 오류가 발생하므로 버전 1.16.16에서 명시적으로 Lombok을 사용하고 싶을 수 있습니다.

3. API 키

Stripe와 통신하고 신용 카드 청구를 실행하려면 Stripe 계정을 등록하고 비밀/공용 Stripe API 키를 얻어야 합니다 .

계정을 확인한 후 로그인하여 Stripe 대시보드 에 액세스합니다 . 그런 다음 왼쪽 메뉴에서 "API 키"를 선택합니다.

스트라이프 대시보드 API 키

두 쌍의 비밀/공개 키 가 있습니다. 하나는 test용이고 다른 하나는 live용 입니다. 나중에 이 키를 사용할 수 있도록 이 탭을 열어 두겠습니다.

4. 일반 흐름

신용 카드 청구는 프런트 엔드(브라우저에서 실행), 백 엔드(Spring Boot 애플리케이션) 및 Stripe를 포함하는 간단한 5단계로 수행됩니다.

  1. 사용자가 체크아웃 페이지로 이동하여 "카드로 결제"를 클릭합니다.
  2. 사용자에게 신용 카드 세부 정보를 채우는 Stripe Checkout 오버레이 대화 상자가 표시됩니다.
  3. 사용자는 "Pay <amount>"로 다음을 확인합니다.
    • Stripe에 신용 카드 보내기
    • 기존 양식에 추가될 응답에서 토큰을 가져옵니다.
    • 금액, 공개 API 키, 이메일 및 토큰과 함께 해당 양식을 백엔드에 제출하십시오.
  4. 우리의 백엔드는 토큰, 금액 및 비밀 API 키로 Stripe에 접속합니다.
  5. 백엔드는 Stripe 응답을 확인하고 사용자에게 작업에 대한 피드백을 제공합니다.
스트라이프 결제 흐름

다음 섹션에서 각 단계를 자세히 다룰 것입니다.

5. 체크아웃 양식

Stripe Checkout신용 카드 세부 정보를 소개하는 양식을 렌더링하는 사용자 지정 가능하고 모바일 지원 및 현지화 가능한 위젯 입니다. " checkout.js " 의 포함 및 구성을 통해 다음 을 담당합니다.

  • "카드로 결제" 버튼 렌더링
    카드결제 버튼
  • 결제 오버레이 대화 상자 렌더링("카드로 결제"를 클릭한 후 트리거됨)
    스트라이프 체크아웃 양식 오버레이
  • 신용 카드 확인
  • "Remember me" 기능(카드를 휴대폰 번호와 연결)
  • Stripe에 신용 카드를 보내고 동봉된 형식의 토큰으로 교체(“Pay <amount>” 클릭 후 트리거됨)

Stripe Checkout에서 제공하는 것보다 체크아웃 양식을 더 많이 제어해야 하는 경우 Stripe Elements 를 사용할 수 있습니다 .

다음으로 양식을 준비하는 컨트롤러와 양식 자체를 분석합니다.

5.1. 제어 장치

결제 양식에 필요한 필수 정보로 모델을 준비 하는 컨트롤러를 생성하여 시작하겠습니다 .

먼저 Stripe 대시보드에서 공개 키의 테스트 버전을 복사하고 STRIPE_PUBLIC_KEY를 환경 변수로 정의하는 데 사용해야 합니다. 그런 다음 stripePublicKey 필드 에서 이 값을 사용 합니다.

여기서는 데모용으로 통화금액 (센트로 표시)을 수동으로 설정 하지만 실제 애플리케이션에서는 실제 값을 가져오는 데 사용할 수 있는 제품/판매 ID를 설정할 수 있습니다.

그런 다음 체크아웃 양식이 있는 체크아웃 보기로 디스패치합니다.

@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. 형태

"Pay with Card" 버튼과 체크아웃 대화 상자는 내부에 스크립트가 있는 양식을 추가하여 포함되며 데이터 속성으로 올바르게 구성됩니다.

<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. 서비스

Stripe에 실제 청구 작업을 전달하기 위해 StripeService 클래스를 작성해 보겠습니다 .

@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 라이브러리에서 반환된 개체는 청구 작업 을 나타내며 작업 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 유형 의 예외를 처리합니다.

더 세분화된 오류 처리가 필요한 경우 CardException , RateLimitException 또는 AuthenticationException 과 같은 StripeException 의 하위 클래스에 대해 별도의 처리기를 추가할 수 있습니다 .

" 결과 " 보기는 청구 작업의 결과를 렌더링합니다.

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 참조 는 전체 작업을 안내합니다.

이 예제에서 사용된 샘플 코드는 GitHub 프로젝트 에서 찾을 수 있습니다 .

Generic footer banner