// // CanPaymentView.swift // SodaLive // // Created by klaus on 2023/08/11. // import SwiftUI import StoreKit struct CanPaymentView: View { @StateObject var viewModel = CanPaymentViewModel() @StateObject var storeManager = StoreManager() let canProduct: SKProduct let refresh: () -> Void let afterCompletionToGoBack: Bool init(canProduct: SKProduct, refresh: @escaping () -> Void, afterCompletionToGoBack: Bool) { self.canProduct = canProduct self.refresh = refresh self.afterCompletionToGoBack = afterCompletionToGoBack } var body: some View { ZStack { Color.black.ignoresSafeArea() GeometryReader { proxy in VStack(spacing: 0) { DetailNavigationBar(title: "결제하기") ScrollView(.vertical, showsIndicators: false) { VStack(spacing: 0) { HStack(spacing: 0) { Image("ic_can") .resizable() .scaledToFill() .frame(width: 26.7, height: 26.7, alignment: .top) Text(canProduct.localizedTitle) .font(.custom(Font.bold.rawValue, size: 15.3)) .foregroundColor(Color(hex: "eeeeee")) .padding(.leading, 13.3) Spacer() Text("\(NumberFormatter.localizedString(from: canProduct.price, number: .currency))") .font(.custom(Font.bold.rawValue, size: 15.3)) .foregroundColor(Color(hex: "eeeeee")) } .padding(.horizontal, 13.3) .padding(.vertical, 23.3) .background(Color(hex: "222222")) .cornerRadius(16.7) .padding(.horizontal, 13.3) .frame(width: screenSize().width) .padding(.top, 13.3) HStack(spacing: 6.7) { Image(viewModel.isTermsAgree ? "btn_select_checked" : "btn_select_normal") .resizable() .frame(width: 20, height: 20) Text("구매조건 확인 및 결제 진행 동의") .font(.custom(Font.medium.rawValue, size: 14.7)) .foregroundColor(Color(hex: "eeeeee")) } .frame(width: screenSize().width - 53.4, alignment: .leading) .padding(.top, 13.3) .onTapGesture { viewModel.isTermsAgree.toggle() } VStack(spacing: 6.7) { HStack(alignment: .top, spacing: 0) { Text("- ") .font(.custom(Font.medium.rawValue, size: 12)) .foregroundColor(Color(hex: "777777")) Text("충전된 코인의 유효기간은 충전 후 5년 입니다.") .font(.custom(Font.medium.rawValue, size: 12)) .foregroundColor(Color(hex: "777777")) .fixedSize(horizontal: false, vertical: true) } .frame(width: screenSize().width - 53.4, alignment: .leading) .padding(.top, 26.7) HStack(alignment: .top, spacing: 0) { Text("- ") .font(.custom(Font.medium.rawValue, size: 12)) .foregroundColor(Color(hex: "777777")) Text("결제 취소는 결제 후 7일 이내에만 할 수 있습니다.\n단, 코인의 일부를 사용하면 결제 취소를 할 수 없습니다.") .font(.custom(Font.medium.rawValue, size: 12)) .foregroundColor(Color(hex: "777777")) .fixedSize(horizontal: false, vertical: true) } .frame(width: screenSize().width - 53.4, alignment: .leading) HStack(alignment: .top, spacing: 0) { Text("- ") .font(.custom(Font.medium.rawValue, size: 12)) .foregroundColor(Color(hex: "777777")) Text("광고성 이벤트 등 회사가 무료로 지급한 포인트는 환불되지 않습니다.") .font(.custom(Font.medium.rawValue, size: 12)) .foregroundColor(Color(hex: "777777")) .fixedSize(horizontal: false, vertical: true) } .frame(width: screenSize().width - 53.4, alignment: .leading) HStack(alignment: .top, spacing: 0) { Text("- ") .font(.custom(Font.medium.rawValue, size: 12)) .foregroundColor(Color(hex: "777777")) Text("자세한 내용은 요즘라이브 이용약관에서 확인할 수 있습니다.") .font(.custom(Font.medium.rawValue, size: 12)) .foregroundColor(Color(hex: "777777")) .fixedSize(horizontal: false, vertical: true) } .frame(width: screenSize().width - 53.4, alignment: .leading) } } } Spacer() HStack(spacing: 0) { VStack(alignment: .leading, spacing: 5) { Text("결제금액") .font(.custom(Font.medium.rawValue, size: 13.3)) .foregroundColor(Color(hex: "eeeeee")) HStack(spacing: 0) { Text("\(NumberFormatter.localizedString(from: canProduct.price, number: .currency))") .font(.custom(Font.bold.rawValue, size: 23.3)) .foregroundColor(Color(hex: "eeeeee")) } } Spacer() Text("결제하기") .font(.custom(Font.bold.rawValue, size: 18.3)) .foregroundColor(.white) .padding(.vertical, 16) .frame(minWidth: 200) .background(Color(hex: "9970ff")) .cornerRadius(10) .onTapGesture { if viewModel.isTermsAgree { viewModel.chargeCan(canProduct: canProduct, paymentGateway: .APPLE_IAP) { product, chargeId in storeManager.payment(product: product, chargeId: chargeId) } } else { viewModel.errorMessage = "결제진행에 동의하셔야 결제가 가능합니다." viewModel.isShowPopup = true } } } .padding(.leading, 22) .padding(.trailing, 13.3) .padding(.vertical, 13.3) .background(Color(hex: "222222")) .cornerRadius(16.7, corners: [.topLeft, .topRight]) if proxy.safeAreaInsets.bottom > 0 { Rectangle() .foregroundColor(Color(hex: "222222")) .frame(width: proxy.size.width, height: 15.3) } } .popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .top, autohideIn: 2) { GeometryReader { geo in HStack { Spacer() Text(viewModel.errorMessage) .padding(.vertical, 13.3) .padding(.horizontal, 6.7) .frame(width: geo.size.width - 66.7, alignment: .center) .font(.custom(Font.medium.rawValue, size: 12)) .background(Color(hex: "9970ff")) .foregroundColor(Color.white) .multilineTextAlignment(.leading) .fixedSize(horizontal: false, vertical: true) .cornerRadius(20) .padding(.top, 66.7) Spacer() } } } .edgesIgnoringSafeArea(.bottom) } if viewModel.isLoading || storeManager.isLoading { LoadingView() } } .popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .top, autohideIn: 2) { GeometryReader { geo in HStack { Spacer() Text(viewModel.errorMessage) .padding(.vertical, 13.3) .padding(.horizontal, 6.7) .frame(width: geo.size.width - 66.7, alignment: .center) .font(.custom(Font.medium.rawValue, size: 12)) .background(Color(hex: "9970ff")) .foregroundColor(Color.white) .multilineTextAlignment(.leading) .fixedSize(horizontal: false, vertical: true) .cornerRadius(20) .padding(.top, 66.7) Spacer() } } } .popup(isPresented: $storeManager.isShowPopup, type: .toast, position: .top, autohideIn: 2) { GeometryReader { geo in HStack { Spacer() Text(storeManager.errorMessage) .padding(.vertical, 13.3) .padding(.horizontal, 6.7) .frame(width: geo.size.width - 66.7, alignment: .center) .font(.custom(Font.medium.rawValue, size: 12)) .background(Color(hex: "9970ff")) .foregroundColor(Color.white) .multilineTextAlignment(.leading) .fixedSize(horizontal: false, vertical: true) .cornerRadius(20) .padding(.top, 66.7) Spacer() } } } .onAppear { storeManager.onSuccessPayment = { receiptString, chargeId in viewModel.verifyPayment(receiptString: receiptString, chargeId: chargeId) { self.refresh() if afterCompletionToGoBack { AppState.shared.back() } else { AppState.shared.setAppStep(step: .canStatus(refresh: refresh)) } let chargeCan = Int(canProduct.localizedDescription) ?? 0 let can = UserDefaults.int(forKey: .can) UserDefaults.set(can + chargeCan, forKey: .can) } } } } } struct CanPaymentView_Previews: PreviewProvider { static var previews: some View { CanPaymentView( canProduct: SKProduct(), refresh: {}, afterCompletionToGoBack: false ) } }