実現したいこと
有料会員機能実装するに当たってstripeでのサブスク継続課金処理、カード情報を取得、取得したカード情報はユーザー情報として保管し、変更削除をできるようにするのが最終着地です。
発生している問題・分からないこと
現在、stirpeで決済処理ができるようにはなっています。
その際にユーザーのステータスの切り替えを同時行なっております。
次のステップとしてアプリ側でユーザーに決済に使用中のカード情報の表示、ユーザーがカード情報変更、削除を出来るようにしたいのですが実装方法が分かりません。
該当のソースコード
Java
1@Service 2public class StripeService { 3private final UserRepository userRepository; 4 5public StripeService(UserRepository userRepository) { 6this.userRepository = userRepository; 7} 8 9public String processSessionCompleted(Event event) { 10Session session = (Session) event.getDataObjectDeserializer().getObject().get(); 11return session.getCustomerEmail(); 12} 13 14@Value("${stripe.api-key}") 15private String stripeApiKey; 16 17public String createStripeSession(String userName, HttpServletRequest httpServletRequest) throws StripeException { 18Stripe.apiKey = stripeApiKey; 19 20String requestUrl = new String(httpServletRequest.getRequestURL()); 21 22// リクエストURLを元にベースURLを設定 23String baseUrl = requestUrl.replaceAll("/membership/subscribe", ""); 24 25// 成功およびキャンセル時のURL設定 26String successUrl = baseUrl + "/membership/subscribe-success?session_id={CHECKOUT_SESSION_ID}"; 27String cancelUrl = baseUrl + "/membership/subscribe?error=payment_cancelled"; 28System.out.println(successUrl); 29System.out.println(cancelUrl); 30 31SessionCreateParams params = SessionCreateParams.builder() 32.addPaymentMethodType(SessionCreateParams.PaymentMethodType.CARD) 33.setMode(SessionCreateParams.Mode.SUBSCRIPTION) 34.setSuccessUrl(successUrl) 35.setCancelUrl(cancelUrl) 36.addLineItem( 37SessionCreateParams.LineItem.builder() 38.setPriceData( 39SessionCreateParams.LineItem.PriceData.builder() 40 .setCurrency("jpy") 41 .setUnitAmount(300L) 42 .setRecurring( 43 SessionCreateParams.LineItem.PriceData.Recurring.builder() 44 .setInterval( 45 SessionCreateParams.LineItem.PriceData.Recurring.Interval.MONTH) 46 .build()) 47 48 .setProductData( 49 SessionCreateParams.LineItem.PriceData.ProductData.builder() 50 .setName("NAGOYAMESHI") 51 .build()) 52 .build()) 53.setQuantity(1L) 54.build()) 55.build(); 56try { 57Session session = Session.create(params); 58return session.getId(); 59} catch (StripeException e) { 60e.printStackTrace(); 61return ""; 62} 63}
Java
1@Service 2public class MembershipService { 3 private final UserRepository userRepository; 4 private final RoleRepository roleRepository; 5 private final UserDetailsService userDetailsService; 6 private final StripeService stripeService; 7 private final UserService userService; 8 9 public MembershipService(UserRepository userRepository, RoleRepository roleRepository, 10 UserDetailsService userDetailsService, StripeService stripeService, UserService userService) { 11 this.userRepository = userRepository; 12 this.roleRepository = roleRepository; 13 this.userDetailsService = userDetailsService; 14 this.stripeService = stripeService; 15 this.userService = userService; 16 } 17 18 public void updateMembershipStatus(String email, String customerId) { 19 User user = userRepository.findByEmail(email); 20 if (user != null) { 21 Role paidMemberRole = roleRepository.findByName("ROLE_PAID_MEMBER"); 22 user.setRole(paidMemberRole); 23 user.setIsPaidMember(true); 24 25 try { 26 // カード情報の取得と保存 27 Customer customer = Customer.retrieve(customerId); 28 PaymentMethod paymentMethod = PaymentMethod 29 .retrieve(customer.getInvoiceSettings().getDefaultPaymentMethod()); 30 31 user.setCardLast4(paymentMethod.getCard().getLast4()); 32 user.setCardBrand(paymentMethod.getCard().getBrand()); 33 user.setCardExpMonth(paymentMethod.getCard().getExpMonth().intValue()); 34 user.setCardExpYear(paymentMethod.getCard().getExpYear().intValue()); 35 } catch (StripeException e) { 36 // エラーハンドリング 37 e.printStackTrace(); 38 } 39 40 userRepository.save(user); 41 } 42 } 43}
Java
1@Controller 2@RequestMapping("/membership") 3public class MembershipController { 4 private final StripeService stripeService; 5 private final MembershipService membershipService; 6 private final UserService userService; 7 8 public MembershipController(MembershipService membershipService, StripeService stripeService, 9 UserService userService) { 10 this.membershipService = membershipService; 11 this.stripeService = stripeService; 12 this.userService = userService; 13 } 14 15 @GetMapping("/subscribe") 16 public String showSubscribePage(Principal principal, HttpServletRequest request, 17 @RequestParam(required = false) String error, Model model) throws StripeException { 18 if (error != null && error.equals("payment_cancelled")) { 19 model.addAttribute("errorMessage", "決済がキャンセルされました。"); 20 } 21 String userName = principal.getName(); 22 String sessionId = stripeService.createStripeSession(userName, request); 23 model.addAttribute("sessionId", sessionId); 24 System.out.println(sessionId); 25 return "membership/subscribe"; 26 } 27 28 @PostMapping("/subscribe") 29 public String subscribe(Principal principal, HttpServletRequest request, Model model) throws StripeException { 30 String userName = principal.getName(); 31 String sessionId = stripeService.createStripeSession(userName, request); 32 model.addAttribute("sessionId", sessionId); 33 System.out.println(sessionId); 34 return "membership/subscribe"; 35 } 36 37 @GetMapping("/subscribe-success") 38 public String subscribeSuccess(@RequestParam("session_id") String sessionId, 39 Principal principal, Model model) { 40 try { 41 String userEmail = principal.getName(); 42 String customerId = stripeService.getCustomerIdFromSession(sessionId); 43 membershipService.updateMembershipStatus(userEmail, customerId); 44 return "redirect:/membership/info?session_id=" + sessionId; 45 } catch (StripeException e) { 46 // エラーハンドリング 47 e.printStackTrace(); 48 return "error-page"; 49 } 50 } 51 52 @GetMapping("/info") 53 public String showMembershipInfo(Principal principal, Model model, 54 @RequestParam(required = false) String session_id) throws StripeException { 55 User user = userService.findByEmail(principal.getName()); 56 model.addAttribute("user", user); 57 58 if (user.isPaidMember()) { 59 PaymentMethod paymentMethod = stripeService.getPaymentMethod(user.getStripeCustomerId()); 60 model.addAttribute("paymentMethod", paymentMethod); 61 } 62 63 if (session_id != null) { 64 model.addAttribute("paymentMessage", "決済が完了しました。"); 65 } 66 67 return "membership/info"; 68 } 69}
試したこと・調べたこと
- teratailやGoogle等で検索した
- ソースコードを自分なりに変更した
- 知人に聞いた
- その他
上記の詳細・結果
以下の記事が私のやりたいことに一番近いものだと思うのですが、実際のコード等がないので、イメージができませんでした。
初回に支払いとカード登録の両方、後にも請求する
①アプリケーションが カード入力フォームを返す前に 空の Customerを作り、②支払いを表す PaymentIntentを生成する
③顧客のリクエストに対し、④Stripe Element(Stripe.js)を使ってカード入力フォームを返す
⑤顧客はカード情報をStripeに送信する
⑥Stripeがカード情報をCustomerに紐づける
⑦Stripeはカード情報から PaymentMethod を返す(アプリケーションはPaymentMethodは必須ではないので、サーバーに送らなくてもよい)
見ての通り、カード番号などセンシティブな情報はアプリケーションに送らず、トークン(PaymentMethod)という形でアプリケーションに送るため、アプリケーションはセンシティブな情報を保持せず、カード決済が行なえるという仕組みになっています。
補足
特になし
あなたの回答
tips
プレビュー