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 title: String? = null | ||||
|  | ||||
|     var googleProductId: String? = null | ||||
| } | ||||
|   | ||||
| @@ -59,6 +59,25 @@ class ChargeController(private val service: ChargeService) { | ||||
|             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 | ||||
|     fun googleCharge(member: Member, request: GoogleChargeRequest) { | ||||
|         val charge = Charge(request.chargeCan, 0) | ||||
|         charge.title = request.title | ||||
|     fun googleCharge( | ||||
|         member: Member, | ||||
|         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 | ||||
|  | ||||
|         val payment = Payment(paymentGateway = request.paymentGateway) | ||||
|         payment.locale = request.currencyCode | ||||
|         payment.price = request.price | ||||
|         payment.receiptId = request.purchaseToken | ||||
|         val payment = Payment(paymentGateway = paymentGateway) | ||||
|         payment.locale = currencyCode | ||||
|         payment.price = price | ||||
|         payment.receiptId = purchaseToken | ||||
|         payment.method = "구글(인 앱 결제)" | ||||
|  | ||||
|         charge.payment = payment | ||||
|         chargeRepository.save(charge) | ||||
|  | ||||
|         if (request.paymentGateway == PaymentGateway.GOOGLE_IAP) { | ||||
|             try { | ||||
|                 val response = androidPublisher.purchases().products() | ||||
|                     .get("kr.co.vividnext.sodalive", request.productId, request.purchaseToken) | ||||
|                     .execute() ?: throw SodaException("결제정보에 오류가 있습니다.") | ||||
|         return charge.id!! | ||||
|     } | ||||
|  | ||||
|                 if ( | ||||
|                     response.purchaseState == 0 && | ||||
|                     response.consumptionState == 1 && | ||||
|                     payment.status == PaymentStatus.REQUEST | ||||
|                 ) { | ||||
|                     payment.status = PaymentStatus.COMPLETE | ||||
|                     member.charge(request.chargeCan, 0, "aos") | ||||
|     @Transactional | ||||
|     fun googleVerify( | ||||
|         memberId: Long, | ||||
|         chargeId: Long, | ||||
|         productId: String, | ||||
|         purchaseToken: String, | ||||
|         paymentGateway: PaymentGateway | ||||
|     ) { | ||||
|         val charge = chargeRepository.findByIdOrNull(id = chargeId) | ||||
|             ?: throw SodaException("결제정보에 오류가 있습니다.") | ||||
|         val member = memberRepository.findByIdOrNull(id = memberId) | ||||
|             ?: throw SodaException("로그인 정보를 확인해주세요.") | ||||
|  | ||||
|                     applicationEventPublisher.publishEvent( | ||||
|                         ChargeSpringEvent( | ||||
|                             chargeId = charge.id!!, | ||||
|                             memberId = member.id!! | ||||
|                         ) | ||||
|                     ) | ||||
|                 } else { | ||||
|                     throw SodaException("결제정보에 오류가 있습니다.") | ||||
|                 } | ||||
|             } catch (e: Exception) { | ||||
|                 e.printStackTrace() | ||||
|         if (paymentGateway == PaymentGateway.GOOGLE_IAP) { | ||||
|             val response = androidPublisher.purchases().products() | ||||
|                 .get("kr.co.vividnext.sodalive", productId, purchaseToken) | ||||
|                 .execute() ?: throw SodaException("결제정보에 오류가 있습니다.") | ||||
|  | ||||
|             if ( | ||||
|                 response.purchaseState == 0 && | ||||
|                 charge.payment!!.status == PaymentStatus.REQUEST | ||||
|             ) { | ||||
|                 consumeWithRetry(productId, purchaseToken, charge, member) | ||||
|             } else { | ||||
|                 throw SodaException("결제정보에 오류가 있습니다.") | ||||
|             } | ||||
|         } 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 { | ||||
|         val body = JSONObject() | ||||
|         body.put("receipt-data", verifyRequest.receiptString) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user