Merge pull request '구글 인 앱 결제 검증' (#151) from test into main
Reviewed-on: #151
This commit is contained in:
		| @@ -42,4 +42,6 @@ data class Charge( | |||||||
|     var useCan: UseCan? = null |     var useCan: UseCan? = null | ||||||
|  |  | ||||||
|     var title: String? = null |     var title: String? = null | ||||||
|  |  | ||||||
|  |     var googleProductId: String? = null | ||||||
| } | } | ||||||
|   | |||||||
| @@ -59,6 +59,25 @@ class ChargeController(private val service: ChargeService) { | |||||||
|             throw SodaException("로그인 정보를 확인해주세요.") |             throw SodaException("로그인 정보를 확인해주세요.") | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         ApiResponse.ok(service.googleCharge(member, request)) |         val chargeId = service.googleCharge( | ||||||
|  |             member = member, | ||||||
|  |             title = request.title, | ||||||
|  |             chargeCan = request.chargeCan, | ||||||
|  |             price = request.price, | ||||||
|  |             currencyCode = request.currencyCode, | ||||||
|  |             productId = request.productId, | ||||||
|  |             purchaseToken = request.purchaseToken, | ||||||
|  |             paymentGateway = request.paymentGateway | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         ApiResponse.ok( | ||||||
|  |             service.googleVerify( | ||||||
|  |                 memberId = member.id!!, | ||||||
|  |                 chargeId = chargeId, | ||||||
|  |                 productId = request.productId, | ||||||
|  |                 purchaseToken = request.purchaseToken, | ||||||
|  |                 paymentGateway = request.paymentGateway | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -187,45 +187,57 @@ class ChargeService( | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Transactional |     @Transactional | ||||||
|     fun googleCharge(member: Member, request: GoogleChargeRequest) { |     fun googleCharge( | ||||||
|         val charge = Charge(request.chargeCan, 0) |         member: Member, | ||||||
|         charge.title = request.title |         title: String, | ||||||
|  |         chargeCan: Int, | ||||||
|  |         price: Double, | ||||||
|  |         currencyCode: String, | ||||||
|  |         productId: String, | ||||||
|  |         purchaseToken: String, | ||||||
|  |         paymentGateway: PaymentGateway | ||||||
|  |     ): Long { | ||||||
|  |         val charge = Charge(chargeCan, 0) | ||||||
|  |         charge.title = title | ||||||
|  |         charge.googleProductId = productId | ||||||
|         charge.member = member |         charge.member = member | ||||||
|  |  | ||||||
|         val payment = Payment(paymentGateway = request.paymentGateway) |         val payment = Payment(paymentGateway = paymentGateway) | ||||||
|         payment.locale = request.currencyCode |         payment.locale = currencyCode | ||||||
|         payment.price = request.price |         payment.price = price | ||||||
|         payment.receiptId = request.purchaseToken |         payment.receiptId = purchaseToken | ||||||
|         payment.method = "구글(인 앱 결제)" |         payment.method = "구글(인 앱 결제)" | ||||||
|  |  | ||||||
|         charge.payment = payment |         charge.payment = payment | ||||||
|         chargeRepository.save(charge) |         chargeRepository.save(charge) | ||||||
|  |  | ||||||
|         if (request.paymentGateway == PaymentGateway.GOOGLE_IAP) { |         return charge.id!! | ||||||
|             try { |     } | ||||||
|                 val response = androidPublisher.purchases().products() |  | ||||||
|                     .get("kr.co.vividnext.sodalive", request.productId, request.purchaseToken) |  | ||||||
|                     .execute() ?: throw SodaException("결제정보에 오류가 있습니다.") |  | ||||||
|  |  | ||||||
|                 if ( |     @Transactional | ||||||
|                     response.purchaseState == 0 && |     fun googleVerify( | ||||||
|                     response.consumptionState == 1 && |         memberId: Long, | ||||||
|                     payment.status == PaymentStatus.REQUEST |         chargeId: Long, | ||||||
|                 ) { |         productId: String, | ||||||
|                     payment.status = PaymentStatus.COMPLETE |         purchaseToken: String, | ||||||
|                     member.charge(request.chargeCan, 0, "aos") |         paymentGateway: PaymentGateway | ||||||
|  |     ) { | ||||||
|  |         val charge = chargeRepository.findByIdOrNull(id = chargeId) | ||||||
|  |             ?: throw SodaException("결제정보에 오류가 있습니다.") | ||||||
|  |         val member = memberRepository.findByIdOrNull(id = memberId) | ||||||
|  |             ?: throw SodaException("로그인 정보를 확인해주세요.") | ||||||
|  |  | ||||||
|                     applicationEventPublisher.publishEvent( |         if (paymentGateway == PaymentGateway.GOOGLE_IAP) { | ||||||
|                         ChargeSpringEvent( |             val response = androidPublisher.purchases().products() | ||||||
|                             chargeId = charge.id!!, |                 .get("kr.co.vividnext.sodalive", productId, purchaseToken) | ||||||
|                             memberId = member.id!! |                 .execute() ?: throw SodaException("결제정보에 오류가 있습니다.") | ||||||
|                         ) |  | ||||||
|                     ) |             if ( | ||||||
|                 } else { |                 response.purchaseState == 0 && | ||||||
|                     throw SodaException("결제정보에 오류가 있습니다.") |                 charge.payment!!.status == PaymentStatus.REQUEST | ||||||
|                 } |             ) { | ||||||
|             } catch (e: Exception) { |                 consumeWithRetry(productId, purchaseToken, charge, member) | ||||||
|                 e.printStackTrace() |             } else { | ||||||
|                 throw SodaException("결제정보에 오류가 있습니다.") |                 throw SodaException("결제정보에 오류가 있습니다.") | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
| @@ -233,6 +245,45 @@ class ChargeService( | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private fun consumeWithRetry(productId: String, purchaseToken: String, charge: Charge, member: Member) { | ||||||
|  |         var attempt = 0 | ||||||
|  |         var delay = 2000L | ||||||
|  |         val retries = 3 | ||||||
|  |  | ||||||
|  |         var lastError: Exception? = null | ||||||
|  |  | ||||||
|  |         while (attempt < retries) { | ||||||
|  |             try { | ||||||
|  |                 androidPublisher.purchases().products().consume( | ||||||
|  |                     "kr.co.vividnext.sodalive", | ||||||
|  |                     productId, | ||||||
|  |                     purchaseToken | ||||||
|  |                 ) | ||||||
|  |                 charge.payment!!.status = PaymentStatus.COMPLETE | ||||||
|  |                 member.charge(charge.chargeCan, 0, "aos") | ||||||
|  |  | ||||||
|  |                 applicationEventPublisher.publishEvent( | ||||||
|  |                     ChargeSpringEvent( | ||||||
|  |                         chargeId = charge.id!!, | ||||||
|  |                         memberId = member.id!! | ||||||
|  |                     ) | ||||||
|  |                 ) | ||||||
|  |  | ||||||
|  |                 return | ||||||
|  |             } catch (e: Exception) { | ||||||
|  |                 lastError = e | ||||||
|  |                 attempt += 1 | ||||||
|  |                 Thread.sleep(delay) | ||||||
|  |                 delay *= 2 | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         lastError?.printStackTrace() | ||||||
|  |  | ||||||
|  |         if (attempt == retries) { | ||||||
|  |             throw SodaException("구매를 하지 못했습니다.\n고객센터로 문의해 주세요") | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     private fun requestRealServerVerify(verifyRequest: AppleVerifyRequest): Boolean { |     private fun requestRealServerVerify(verifyRequest: AppleVerifyRequest): Boolean { | ||||||
|         val body = JSONObject() |         val body = JSONObject() | ||||||
|         body.put("receipt-data", verifyRequest.receiptString) |         body.put("receipt-data", verifyRequest.receiptString) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user