콘텐츠 상세, 콘텐츠 구매
- pg 테스트 계정의 경우 캔이 아닌 원으로 표시되도록 하고 콘텐츠 구매시 바로 결제 후 구매 되도록 수정
This commit is contained in:
		| @@ -127,4 +127,6 @@ enum AppStep { | |||||||
|     case seriesAll(creatorId: Int) |     case seriesAll(creatorId: Int) | ||||||
|      |      | ||||||
|     case seriesContentAll(seriesId: Int, seriesTitle: String) |     case seriesContentAll(seriesId: Int, seriesTitle: String) | ||||||
|  |          | ||||||
|  |     case tempCanPayment(title: String, can: Int, onSuccess: () -> Void) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -23,7 +23,7 @@ struct ContentDetailCreatorProfileView: View { | |||||||
|              |              | ||||||
|             Text(creator.nickname) |             Text(creator.nickname) | ||||||
|                 .font(.custom(Font.medium.rawValue, size: 12)) |                 .font(.custom(Font.medium.rawValue, size: 12)) | ||||||
|                 .foregroundColor(Color(hex: "777777")) |                 .foregroundColor(Color.gray77) | ||||||
|                 .padding(.horizontal, 5.3) |                 .padding(.horizontal, 5.3) | ||||||
|              |              | ||||||
|             Spacer() |             Spacer() | ||||||
|   | |||||||
| @@ -14,16 +14,18 @@ struct ContentDetailPurchaseButton: View { | |||||||
|      |      | ||||||
|     var body: some View { |     var body: some View { | ||||||
|         HStack(spacing: 0) { |         HStack(spacing: 0) { | ||||||
|             Image("ic_can") |             if UserDefaults.int(forKey: .userId) != 17958 { | ||||||
|                 .resizable() |                 Image("ic_can") | ||||||
|                 .frame(width: 16.7, height: 16.7) |                     .resizable() | ||||||
|  |                     .frame(width: 16.7, height: 16.7) | ||||||
|  |             } | ||||||
|              |              | ||||||
|             Text("\(price)") |             Text(UserDefaults.int(forKey: .userId) == 17958 ? "\(price * 110)" : "\(price)") | ||||||
|                 .font(.custom(Font.bold.rawValue, size: 14.7)) |                 .font(.custom(Font.bold.rawValue, size: 14.7)) | ||||||
|                 .foregroundColor(.white) |                 .foregroundColor(.white) | ||||||
|                 .padding(.leading, 5.3) |                 .padding(.leading, 5.3) | ||||||
|              |              | ||||||
|             Text("캔으로") |             Text(UserDefaults.int(forKey: .userId) == 17958 ? "원으로": "캔으로") | ||||||
|                 .font(.custom(Font.light.rawValue, size: 12)) |                 .font(.custom(Font.light.rawValue, size: 12)) | ||||||
|                 .foregroundColor(.white) |                 .foregroundColor(.white) | ||||||
|              |              | ||||||
|   | |||||||
| @@ -38,7 +38,7 @@ struct ContentDetailView: View { | |||||||
|                              |                              | ||||||
|                             Text("콘텐츠 상세") |                             Text("콘텐츠 상세") | ||||||
|                                 .font(.custom(Font.bold.rawValue, size: 18.3)) |                                 .font(.custom(Font.bold.rawValue, size: 18.3)) | ||||||
|                                 .foregroundColor(Color(hex: "eeeeee")) |                                 .foregroundColor(Color.grayee) | ||||||
|                         } |                         } | ||||||
|                          |                          | ||||||
|                         Spacer() |                         Spacer() | ||||||
| @@ -99,7 +99,7 @@ struct ContentDetailView: View { | |||||||
|                                                 .foregroundColor(.white) |                                                 .foregroundColor(.white) | ||||||
|                                                 .frame(maxWidth: .infinity) |                                                 .frame(maxWidth: .infinity) | ||||||
|                                                 .frame(height: 48.7) |                                                 .frame(height: 48.7) | ||||||
|                                                 .background(Color(hex: "525252")) |                                                 .background(Color.gray52) | ||||||
|                                                 .cornerRadius(5.3) |                                                 .cornerRadius(5.3) | ||||||
|                                                 .padding(.top, 18.3) |                                                 .padding(.top, 18.3) | ||||||
|                                                 .padding(.horizontal, 13.3) |                                                 .padding(.horizontal, 13.3) | ||||||
| @@ -113,7 +113,7 @@ struct ContentDetailView: View { | |||||||
|                                                     .foregroundColor(.white) |                                                     .foregroundColor(.white) | ||||||
|                                                     .frame(maxWidth: .infinity) |                                                     .frame(maxWidth: .infinity) | ||||||
|                                                     .frame(height: 48.7) |                                                     .frame(height: 48.7) | ||||||
|                                                     .background(Color(hex: "525252")) |                                                     .background(Color.gray52) | ||||||
|                                                     .cornerRadius(5.3) |                                                     .cornerRadius(5.3) | ||||||
|                                                     .padding(.top, 18.3) |                                                     .padding(.top, 18.3) | ||||||
|                                                     .padding(.horizontal, 13.3) |                                                     .padding(.horizontal, 13.3) | ||||||
| @@ -162,7 +162,7 @@ struct ContentDetailView: View { | |||||||
|                                         .padding(.horizontal, 13.3) |                                         .padding(.horizontal, 13.3) | ||||||
|                                          |                                          | ||||||
|                                         Rectangle() |                                         Rectangle() | ||||||
|                                             .foregroundColor(Color(hex: "232323")) |                                             .foregroundColor(Color.gray23) | ||||||
|                                             .frame(height: 6.7) |                                             .frame(height: 6.7) | ||||||
|                                             .padding(.top, 24) |                                             .padding(.top, 24) | ||||||
|                                          |                                          | ||||||
| @@ -207,7 +207,7 @@ struct ContentDetailView: View { | |||||||
|                          |                          | ||||||
|                         if proxy.safeAreaInsets.bottom > 0 { |                         if proxy.safeAreaInsets.bottom > 0 { | ||||||
|                             Rectangle() |                             Rectangle() | ||||||
|                                 .foregroundColor(Color(hex: "222222")) |                                 .foregroundColor(Color.gray22) | ||||||
|                                 .frame(width: proxy.size.width, height: 15.3) |                                 .frame(width: proxy.size.width, height: 15.3) | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
| @@ -226,7 +226,20 @@ struct ContentDetailView: View { | |||||||
|                             orderType: orderType, |                             orderType: orderType, | ||||||
|                             isOnlyRental: audioContent.isOnlyRental, |                             isOnlyRental: audioContent.isOnlyRental, | ||||||
|                             onClickConfirm: { |                             onClickConfirm: { | ||||||
|                                 viewModel.order(orderType: orderType) |                                 if UserDefaults.int(forKey: .userId) == 17958 { | ||||||
|  |                                     AppState.shared | ||||||
|  |                                         .setAppStep( | ||||||
|  |                                             step: .tempCanPayment( | ||||||
|  |                                                 title: audioContent.title, | ||||||
|  |                                                 can: orderType == .RENTAL ? Int(ceil(Double(audioContent.price) * 0.6)) : audioContent.price, | ||||||
|  |                                                 onSuccess: { | ||||||
|  |                                                     viewModel.order(orderType: orderType) | ||||||
|  |                                                 } | ||||||
|  |                                             ) | ||||||
|  |                                         ) | ||||||
|  |                                 } else { | ||||||
|  |                                     viewModel.order(orderType: orderType) | ||||||
|  |                                 } | ||||||
|                             } |                             } | ||||||
|                         ) |                         ) | ||||||
|                     } |                     } | ||||||
| @@ -272,7 +285,7 @@ struct ContentDetailView: View { | |||||||
|                              |                              | ||||||
|                             if proxy.safeAreaInsets.bottom > 0 { |                             if proxy.safeAreaInsets.bottom > 0 { | ||||||
|                                 Rectangle() |                                 Rectangle() | ||||||
|                                     .foregroundColor(Color(hex: "222222")) |                                     .foregroundColor(Color.gray22) | ||||||
|                                     .frame(width: proxy.size.width, height: 15.3) |                                     .frame(width: proxy.size.width, height: 15.3) | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|   | |||||||
| @@ -27,7 +27,7 @@ struct ContentOrderConfirmDialogView: View { | |||||||
|             VStack(spacing: 0) { |             VStack(spacing: 0) { | ||||||
|                 Text("구매확인") |                 Text("구매확인") | ||||||
|                     .font(.custom(Font.bold.rawValue, size: 18.3)) |                     .font(.custom(Font.bold.rawValue, size: 18.3)) | ||||||
|                     .foregroundColor(Color(hex: "eeeeee")) |                     .foregroundColor(Color.grayee) | ||||||
|                  |                  | ||||||
|                 HStack(spacing: 11) { |                 HStack(spacing: 11) { | ||||||
|                     ZStack(alignment: .topLeading) { |                     ZStack(alignment: .topLeading) { | ||||||
| @@ -48,7 +48,7 @@ struct ContentOrderConfirmDialogView: View { | |||||||
|                          |                          | ||||||
|                         Text(audioContent.title) |                         Text(audioContent.title) | ||||||
|                             .font(.custom(Font.bold.rawValue, size: 11.3)) |                             .font(.custom(Font.bold.rawValue, size: 11.3)) | ||||||
|                             .foregroundColor(Color(hex: "d2d2d2")) |                             .foregroundColor(Color.grayd2) | ||||||
|                             .padding(.top, 2) |                             .padding(.top, 2) | ||||||
|                          |                          | ||||||
|                         HStack(spacing: 4.3) { |                         HStack(spacing: 4.3) { | ||||||
| @@ -60,13 +60,13 @@ struct ContentOrderConfirmDialogView: View { | |||||||
|                              |                              | ||||||
|                             Text(audioContent.creator.nickname) |                             Text(audioContent.creator.nickname) | ||||||
|                                 .font(.custom(Font.medium.rawValue, size: 10)) |                                 .font(.custom(Font.medium.rawValue, size: 10)) | ||||||
|                                 .foregroundColor(Color(hex: "777777")) |                                 .foregroundColor(Color.gray77) | ||||||
|                         } |                         } | ||||||
|                         .padding(.top, 6.7) |                         .padding(.top, 6.7) | ||||||
|                          |                          | ||||||
|                         Text(audioContent.duration) |                         Text(audioContent.duration) | ||||||
|                             .font(.custom(Font.medium.rawValue, size: 11)) |                             .font(.custom(Font.medium.rawValue, size: 11)) | ||||||
|                             .foregroundColor(Color(hex: "777777")) |                             .foregroundColor(Color.gray77) | ||||||
|                             .padding(.top, 6.7) |                             .padding(.top, 6.7) | ||||||
|                     } |                     } | ||||||
|                      |                      | ||||||
| @@ -77,52 +77,74 @@ struct ContentOrderConfirmDialogView: View { | |||||||
|                 .cornerRadius(5.3) |                 .cornerRadius(5.3) | ||||||
|                 .padding(.top, 21.3) |                 .padding(.top, 21.3) | ||||||
|                  |                  | ||||||
|                 Text("콘텐츠를 \(orderType == .RENTAL ? "대여" : "소장")하시겠습니까?\n아래 캔이 차감됩니다.") |                 Text("콘텐츠를 \(orderType == .RENTAL ? "대여" : "소장")하시겠습니까?") | ||||||
|                     .font(.custom(Font.medium.rawValue, size: 13.3)) |                     .font(.custom(Font.medium.rawValue, size: 13.3)) | ||||||
|                     .foregroundColor(Color(hex: "eeeeee")) |                     .foregroundColor(Color.grayee) | ||||||
|                     .fixedSize(horizontal: false, vertical: true) |                     .fixedSize(horizontal: false, vertical: true) | ||||||
|                     .multilineTextAlignment(.center) |                     .multilineTextAlignment(.center) | ||||||
|                     .padding(.top, 13.3) |                     .padding(.top, 13.3) | ||||||
|                  |                  | ||||||
|  |                 if UserDefaults.int(forKey: .userId) != 17958 { | ||||||
|  |                     Text("아래 캔이 차감됩니다.") | ||||||
|  |                         .font(.custom(Font.medium.rawValue, size: 13.3)) | ||||||
|  |                         .foregroundColor(Color.grayee) | ||||||
|  |                         .fixedSize(horizontal: false, vertical: true) | ||||||
|  |                         .multilineTextAlignment(.center) | ||||||
|  |                         .padding(.top, 13.3) | ||||||
|  |                 } | ||||||
|  |                  | ||||||
|                 HStack(spacing: 2.7) { |                 HStack(spacing: 2.7) { | ||||||
|                     Spacer() |                     Spacer() | ||||||
|                      |                  | ||||||
|                     Image("ic_can") |                     if UserDefaults.int(forKey: .userId) != 17958 { | ||||||
|                         .resizable() |                         Image("ic_can") | ||||||
|                         .frame(width: 16.7, height: 16.7) |                             .resizable() | ||||||
|                      |                             .frame(width: 16.7, height: 16.7) | ||||||
|                     if orderType == .RENTAL { |                          | ||||||
|                         Text("\(isOnlyRental ? audioContent.price : Int(ceil(Double(audioContent.price) * 0.6)))") |                         if orderType == .RENTAL { | ||||||
|                             .font(.custom(Font.bold.rawValue, size: 13.3)) |                             Text("\(isOnlyRental ? audioContent.price : Int(ceil(Double(audioContent.price) * 0.6)))") | ||||||
|                             .foregroundColor(Color(hex: "eeeeee")) |                                 .font(.custom(Font.bold.rawValue, size: 13.3)) | ||||||
|  |                                 .foregroundColor(Color.grayee) | ||||||
|  |                         } else { | ||||||
|  |                             Text("\(audioContent.price)") | ||||||
|  |                                 .font(.custom(Font.bold.rawValue, size: 13.3)) | ||||||
|  |                                 .foregroundColor(Color.grayee) | ||||||
|  |                         } | ||||||
|                     } else { |                     } else { | ||||||
|                         Text("\(audioContent.price)") |                         if orderType == .RENTAL { | ||||||
|                             .font(.custom(Font.bold.rawValue, size: 13.3)) |                             Text("\(isOnlyRental ? audioContent.price * 110 : Int(ceil(Double(audioContent.price) * 0.6)) * 110)원") | ||||||
|                             .foregroundColor(Color(hex: "eeeeee")) |                                 .font(.custom(Font.bold.rawValue, size: 13.3)) | ||||||
|  |                                 .foregroundColor(Color.grayee) | ||||||
|  |                         } else { | ||||||
|  |                             Text("\(audioContent.price * 110)원") | ||||||
|  |                                 .font(.custom(Font.bold.rawValue, size: 13.3)) | ||||||
|  |                                 .foregroundColor(Color.grayee) | ||||||
|  |                         } | ||||||
|                     } |                     } | ||||||
|                      |                      | ||||||
|                     Spacer() |                     Spacer() | ||||||
|                 } |                 } | ||||||
|                 .padding(.vertical, 13.3) |                 .padding(.vertical, 13.3) | ||||||
|                 .background(Color(hex: "333333")) |                 .background(Color.gray33) | ||||||
|                 .cornerRadius(6.7) |                 .cornerRadius(6.7) | ||||||
|                 .overlay( |                 .overlay( | ||||||
|                     RoundedRectangle(cornerRadius: CGFloat(6.7)) |                     RoundedRectangle(cornerRadius: CGFloat(6.7)) | ||||||
|                         .stroke(lineWidth: 1) |                         .stroke(lineWidth: 1) | ||||||
|                         .foregroundColor(Color(hex: "979797")) |                         .foregroundColor(Color.gray97) | ||||||
|                 ) |                 ) | ||||||
|                 .padding(.top, 13.3) |                 .padding(.top, 13.3) | ||||||
|                  |                  | ||||||
|                 HStack(spacing: 12) { |                 HStack(spacing: 12) { | ||||||
|                     Text("취소") |                     Text("취소") | ||||||
|                         .font(.custom(Font.bold.rawValue, size: 18.3)) |                         .font(.custom(Font.bold.rawValue, size: 18.3)) | ||||||
|                         .foregroundColor(Color(hex: "9970ff")) |                         .foregroundColor(Color.button) | ||||||
|                         .padding(.vertical, 15.7) |                         .padding(.vertical, 15.7) | ||||||
|                         .frame(maxWidth: .infinity) |                         .frame(maxWidth: .infinity) | ||||||
|  |                         .contentShape(Rectangle()) | ||||||
|                         .overlay( |                         .overlay( | ||||||
|                             RoundedRectangle(cornerRadius: CGFloat(10)) |                             RoundedRectangle(cornerRadius: CGFloat(10)) | ||||||
|                                 .stroke(lineWidth: 1) |                                 .stroke(lineWidth: 1) | ||||||
|                                 .foregroundColor(Color(hex: "9970ff")) |                                 .foregroundColor(Color.button) | ||||||
|                         ) |                         ) | ||||||
|                         .onTapGesture { isShowing = false } |                         .onTapGesture { isShowing = false } | ||||||
|                      |                      | ||||||
| @@ -131,7 +153,8 @@ struct ContentOrderConfirmDialogView: View { | |||||||
|                         .foregroundColor(.white) |                         .foregroundColor(.white) | ||||||
|                         .padding(.vertical, 15.7) |                         .padding(.vertical, 15.7) | ||||||
|                         .frame(maxWidth: .infinity) |                         .frame(maxWidth: .infinity) | ||||||
|                         .background(Color(hex: "9970ff")) |                         .contentShape(Rectangle()) | ||||||
|  |                         .background(Color.button) | ||||||
|                         .cornerRadius(10) |                         .cornerRadius(10) | ||||||
|                         .onTapGesture { |                         .onTapGesture { | ||||||
|                             onClickConfirm() |                             onClickConfirm() | ||||||
| @@ -143,7 +166,7 @@ struct ContentOrderConfirmDialogView: View { | |||||||
|             .padding(.horizontal, 13.3) |             .padding(.horizontal, 13.3) | ||||||
|             .padding(.top, 26.7) |             .padding(.top, 26.7) | ||||||
|             .padding(.bottom, 16.7) |             .padding(.bottom, 16.7) | ||||||
|             .background(Color(hex: "222222")) |             .background(Color.gray22) | ||||||
|             .cornerRadius(10) |             .cornerRadius(10) | ||||||
|             .padding(.horizontal, 20) |             .padding(.horizontal, 20) | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -40,17 +40,25 @@ struct ContentOrderDialogView: View { | |||||||
|                         Spacer() |                         Spacer() | ||||||
|                          |                          | ||||||
|                         HStack(spacing: 8) { |                         HStack(spacing: 8) { | ||||||
|                             Image("ic_can") |                             if UserDefaults.int(forKey: .userId) != 17958 { | ||||||
|                                 .resizable() |                                 Image("ic_can") | ||||||
|                                 .frame(width: 16.7, height: 16.7) |                                     .resizable() | ||||||
|  |                                     .frame(width: 16.7, height: 16.7) | ||||||
|  |                             } | ||||||
|                              |                              | ||||||
|                             Text(isOnlyRental ? "\(price)" : "\(Int(ceil(Double(price) * 0.6)))") |                             Text(isOnlyRental ? "\(price * 110)" : "\(Int(ceil(Double(price) * 0.6)) * 110)") | ||||||
|                                 .font(.custom(Font.bold.rawValue, size: 13.3)) |                                 .font(.custom(Font.bold.rawValue, size: 13.3)) | ||||||
|                                 .foregroundColor(Color(hex: "eeeeee")) |                                 .foregroundColor(Color.grayee) | ||||||
|  |                              | ||||||
|  |                             if UserDefaults.int(forKey: .userId) == 17958 { | ||||||
|  |                                 Text("원") | ||||||
|  |                                     .font(.custom(Font.bold.rawValue, size: 13.3)) | ||||||
|  |                                     .foregroundColor(Color.grayee) | ||||||
|  |                             } | ||||||
|                         } |                         } | ||||||
|                         .padding(.vertical, 8) |                         .padding(.vertical, 8) | ||||||
|                         .padding(.horizontal, 13.3) |                         .padding(.horizontal, 13.3) | ||||||
|                         .background(Color(hex: "9970ff")) |                         .background(Color.button) | ||||||
|                         .cornerRadius(5.3) |                         .cornerRadius(5.3) | ||||||
|                         .onTapGesture { |                         .onTapGesture { | ||||||
|                             onTapPurchase(.RENTAL) |                             onTapPurchase(.RENTAL) | ||||||
| @@ -73,17 +81,25 @@ struct ContentOrderDialogView: View { | |||||||
|                             Spacer() |                             Spacer() | ||||||
|                              |                              | ||||||
|                             HStack(spacing: 8) { |                             HStack(spacing: 8) { | ||||||
|                                 Image("ic_can") |                                 if UserDefaults.int(forKey: .userId) != 17958 { | ||||||
|                                     .resizable() |                                     Image("ic_can") | ||||||
|                                     .frame(width: 16.7, height: 16.7) |                                         .resizable() | ||||||
|  |                                         .frame(width: 16.7, height: 16.7) | ||||||
|  |                                 } | ||||||
|                                  |                                  | ||||||
|                                 Text("\(price)") |                                 Text("\(price * 110)") | ||||||
|                                     .font(.custom(Font.bold.rawValue, size: 13.3)) |                                     .font(.custom(Font.bold.rawValue, size: 13.3)) | ||||||
|                                     .foregroundColor(Color(hex: "eeeeee")) |                                     .foregroundColor(Color.grayee) | ||||||
|  |                                  | ||||||
|  |                                 if UserDefaults.int(forKey: .userId) == 17958 { | ||||||
|  |                                     Text("원") | ||||||
|  |                                         .font(.custom(Font.bold.rawValue, size: 13.3)) | ||||||
|  |                                         .foregroundColor(Color.grayee) | ||||||
|  |                                 } | ||||||
|                             } |                             } | ||||||
|                             .padding(.vertical, 8) |                             .padding(.vertical, 8) | ||||||
|                             .padding(.horizontal, 13.3) |                             .padding(.horizontal, 13.3) | ||||||
|                             .background(Color(hex: "9970ff")) |                             .background(Color.button) | ||||||
|                             .cornerRadius(5.3) |                             .cornerRadius(5.3) | ||||||
|                             .onTapGesture { |                             .onTapGesture { | ||||||
|                                 onTapPurchase(.KEEP) |                                 onTapPurchase(.KEEP) | ||||||
| @@ -93,7 +109,7 @@ struct ContentOrderDialogView: View { | |||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 .padding(24) |                 .padding(24) | ||||||
|                 .background(Color(hex: "222222")) |                 .background(Color.gray22) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -187,6 +187,10 @@ struct ContentView: View { | |||||||
|             case .seriesContentAll(let seriesId, let seriesTitle): |             case .seriesContentAll(let seriesId, let seriesTitle): | ||||||
|                 SeriesContentAllView(seriesId: seriesId, seriesTitle: seriesTitle) |                 SeriesContentAllView(seriesId: seriesId, seriesTitle: seriesTitle) | ||||||
|                  |                  | ||||||
|  |             case .tempCanPayment(let title, let can, let onSuccess): | ||||||
|  |                 CanPaymentTempView(title: title, can: can, onSuccess: onSuccess) | ||||||
|  |                  | ||||||
|  |                  | ||||||
|             default: |             default: | ||||||
|                 EmptyView() |                 EmptyView() | ||||||
|                     .frame(width: 0, height: 0, alignment: .topLeading) |                     .frame(width: 0, height: 0, alignment: .topLeading) | ||||||
|   | |||||||
| @@ -86,11 +86,11 @@ struct CanPgPaymentView: View { | |||||||
|                                      |                                      | ||||||
|                                     Text("\(canResponse.price) 원") |                                     Text("\(canResponse.price) 원") | ||||||
|                                         .font(.custom(Font.bold.rawValue, size: 15.3)) |                                         .font(.custom(Font.bold.rawValue, size: 15.3)) | ||||||
|                                         .foregroundColor(Color(hex: "eeeeee")) |                                         .foregroundColor(Color.grayee) | ||||||
|                                 } |                                 } | ||||||
|                                 .padding(.horizontal, 13.3) |                                 .padding(.horizontal, 13.3) | ||||||
|                                 .padding(.vertical, 23.3) |                                 .padding(.vertical, 23.3) | ||||||
|                                 .background(Color(hex: "222222")) |                                 .background(Color.gray22) | ||||||
|                                 .cornerRadius(16.7) |                                 .cornerRadius(16.7) | ||||||
|                                 .padding(.horizontal, 13.3) |                                 .padding(.horizontal, 13.3) | ||||||
|                                 .frame(width: screenSize().width) |                                 .frame(width: screenSize().width) | ||||||
| @@ -98,7 +98,7 @@ struct CanPgPaymentView: View { | |||||||
|                                  |                                  | ||||||
|                                 Text("결제 수단 선택") |                                 Text("결제 수단 선택") | ||||||
|                                     .font(.custom(Font.bold.rawValue, size: 16.7)) |                                     .font(.custom(Font.bold.rawValue, size: 16.7)) | ||||||
|                                     .foregroundColor(Color(hex: "eeeeee")) |                                     .foregroundColor(Color.grayee) | ||||||
|                                     .frame(width: screenSize().width - 26.7, alignment: .leading) |                                     .frame(width: screenSize().width - 26.7, alignment: .leading) | ||||||
|                                     .padding(.top, 26.7) |                                     .padding(.top, 26.7) | ||||||
|                                  |                                  | ||||||
| @@ -106,18 +106,19 @@ struct CanPgPaymentView: View { | |||||||
|                                 HStack(spacing: 13.3) { |                                 HStack(spacing: 13.3) { | ||||||
|                                     Text("카드") |                                     Text("카드") | ||||||
|                                         .font(.custom( viewModel.paymentMethod == .card ? Font.bold.rawValue : Font.medium.rawValue, size: 16.7)) |                                         .font(.custom( viewModel.paymentMethod == .card ? Font.bold.rawValue : Font.medium.rawValue, size: 16.7)) | ||||||
|                                         .foregroundColor(Color(hex: viewModel.paymentMethod == .card ? "9970ff" : "eeeeee")) |                                         .foregroundColor(viewModel.paymentMethod == .card ? Color.button : Color.grayee) | ||||||
|                                         .frame(width: (screenSize().width - 40) / 2) |                                         .frame(width: (screenSize().width - 40) / 2) | ||||||
|                                         .padding(.vertical, 16.7) |                                         .padding(.vertical, 16.7) | ||||||
|                                         .background( |                                         .background( | ||||||
|                                             Color(hex: viewModel.paymentMethod == .card ? "9970ff" : "232323") |                                             viewModel.paymentMethod == .card ? | ||||||
|                                                 .opacity(viewModel.paymentMethod == .card ? 0.3 : 1) |                                             Color.button.opacity(0.3) : | ||||||
|  |                                                 Color.gray23 | ||||||
|                                         ) |                                         ) | ||||||
|                                         .cornerRadius(10) |                                         .cornerRadius(10) | ||||||
|                                         .overlay( |                                         .overlay( | ||||||
|                                             RoundedRectangle(cornerRadius: 10) |                                             RoundedRectangle(cornerRadius: 10) | ||||||
|                                                 .stroke(lineWidth: 1) |                                                 .stroke(lineWidth: 1) | ||||||
|                                                 .foregroundColor(Color(hex: viewModel.paymentMethod == .card ? "9970ff" : "777777")) |                                                 .foregroundColor(viewModel.paymentMethod == .card ? Color.button : Color.gray77) | ||||||
|                                         ) |                                         ) | ||||||
|                                         .onTapGesture { |                                         .onTapGesture { | ||||||
|                                             if viewModel.paymentMethod != .card { |                                             if viewModel.paymentMethod != .card { | ||||||
| @@ -128,18 +129,19 @@ struct CanPgPaymentView: View { | |||||||
|                                      |                                      | ||||||
|                                     Text("계좌이체") |                                     Text("계좌이체") | ||||||
|                                         .font(.custom( viewModel.paymentMethod == .bank ? Font.bold.rawValue : Font.medium.rawValue, size: 16.7)) |                                         .font(.custom( viewModel.paymentMethod == .bank ? Font.bold.rawValue : Font.medium.rawValue, size: 16.7)) | ||||||
|                                         .foregroundColor(Color(hex: viewModel.paymentMethod == .bank ? "9970ff" : "eeeeee")) |                                         .foregroundColor(viewModel.paymentMethod == .bank ? Color.button : Color.grayee) | ||||||
|                                         .frame(width: (screenSize().width - 40) / 2) |                                         .frame(width: (screenSize().width - 40) / 2) | ||||||
|                                         .padding(.vertical, 16.7) |                                         .padding(.vertical, 16.7) | ||||||
|                                         .background( |                                         .background( | ||||||
|                                             Color(hex: viewModel.paymentMethod == .bank ? "9970ff" : "232323") |                                             viewModel.paymentMethod == .bank ? | ||||||
|                                                 .opacity(viewModel.paymentMethod == .bank ? 0.3 : 1) |                                             Color.button.opacity(0.3) : | ||||||
|  |                                                 Color.gray23 | ||||||
|                                         ) |                                         ) | ||||||
|                                         .cornerRadius(10) |                                         .cornerRadius(10) | ||||||
|                                         .overlay( |                                         .overlay( | ||||||
|                                             RoundedRectangle(cornerRadius: 10) |                                             RoundedRectangle(cornerRadius: 10) | ||||||
|                                                 .stroke(lineWidth: 1) |                                                 .stroke(lineWidth: 1) | ||||||
|                                                 .foregroundColor(Color(hex: viewModel.paymentMethod == .bank ? "9970ff" : "777777")) |                                                 .foregroundColor(viewModel.paymentMethod == .bank ? Color.button : Color.gray77) | ||||||
|                                         ) |                                         ) | ||||||
|                                         .onTapGesture { |                                         .onTapGesture { | ||||||
|                                             if viewModel.paymentMethod != .bank { |                                             if viewModel.paymentMethod != .bank { | ||||||
| @@ -153,18 +155,19 @@ struct CanPgPaymentView: View { | |||||||
|                                 HStack(spacing: 13.3) { |                                 HStack(spacing: 13.3) { | ||||||
|                                     Text("휴대폰 결제") |                                     Text("휴대폰 결제") | ||||||
|                                         .font(.custom( viewModel.paymentMethod == .phone ? Font.bold.rawValue : Font.medium.rawValue, size: 16.7)) |                                         .font(.custom( viewModel.paymentMethod == .phone ? Font.bold.rawValue : Font.medium.rawValue, size: 16.7)) | ||||||
|                                         .foregroundColor(Color(hex: viewModel.paymentMethod == .phone ? "9970ff" : "eeeeee")) |                                         .foregroundColor(viewModel.paymentMethod == .phone ? Color.button : Color.grayee) | ||||||
|                                         .frame(width: (screenSize().width - 40) / 2) |                                         .frame(width: (screenSize().width - 40) / 2) | ||||||
|                                         .padding(.vertical, 16.7) |                                         .padding(.vertical, 16.7) | ||||||
|                                         .background( |                                         .background( | ||||||
|                                             Color(hex: viewModel.paymentMethod == .phone ? "9970ff" : "232323") |                                             viewModel.paymentMethod == .phone ? | ||||||
|                                                 .opacity(viewModel.paymentMethod == .phone ? 0.3 : 1) |                                             Color.button.opacity(0.3) : | ||||||
|  |                                                 Color.gray23 | ||||||
|                                         ) |                                         ) | ||||||
|                                         .cornerRadius(10) |                                         .cornerRadius(10) | ||||||
|                                         .overlay( |                                         .overlay( | ||||||
|                                             RoundedRectangle(cornerRadius: 10) |                                             RoundedRectangle(cornerRadius: 10) | ||||||
|                                                 .stroke(lineWidth: 1) |                                                 .stroke(lineWidth: 1) | ||||||
|                                                 .foregroundColor(Color(hex: viewModel.paymentMethod == .phone ? "9970ff" : "777777")) |                                                 .foregroundColor(viewModel.paymentMethod == .phone ? Color.button : Color.gray77) | ||||||
|                                         ) |                                         ) | ||||||
|                                         .onTapGesture { |                                         .onTapGesture { | ||||||
|                                             if viewModel.paymentMethod != .phone { |                                             if viewModel.paymentMethod != .phone { | ||||||
| @@ -184,7 +187,7 @@ struct CanPgPaymentView: View { | |||||||
|                                      |                                      | ||||||
|                                     Text("구매조건 확인 및 결제 진행 동의") |                                     Text("구매조건 확인 및 결제 진행 동의") | ||||||
|                                         .font(.custom(Font.medium.rawValue, size: 14.7)) |                                         .font(.custom(Font.medium.rawValue, size: 14.7)) | ||||||
|                                         .foregroundColor(Color(hex: "eeeeee")) |                                         .foregroundColor(Color.grayee) | ||||||
|                                 } |                                 } | ||||||
|                                 .frame(width: screenSize().width - 53.4, alignment: .leading) |                                 .frame(width: screenSize().width - 53.4, alignment: .leading) | ||||||
|                                 .padding(.top, 16.7) |                                 .padding(.top, 16.7) | ||||||
| @@ -196,11 +199,11 @@ struct CanPgPaymentView: View { | |||||||
|                                     HStack(alignment: .top, spacing: 0) { |                                     HStack(alignment: .top, spacing: 0) { | ||||||
|                                         Text("- ") |                                         Text("- ") | ||||||
|                                             .font(.custom(Font.medium.rawValue, size: 12)) |                                             .font(.custom(Font.medium.rawValue, size: 12)) | ||||||
|                                             .foregroundColor(Color(hex: "777777")) |                                             .foregroundColor(Color.gray77) | ||||||
|                                          |                                          | ||||||
|                                         Text("충전된 캔의 유효기간은 충전 후 5년 입니다.") |                                         Text("충전된 캔의 유효기간은 충전 후 5년 입니다.") | ||||||
|                                             .font(.custom(Font.medium.rawValue, size: 12)) |                                             .font(.custom(Font.medium.rawValue, size: 12)) | ||||||
|                                             .foregroundColor(Color(hex: "777777")) |                                             .foregroundColor(Color.gray77) | ||||||
|                                             .fixedSize(horizontal: false, vertical: true) |                                             .fixedSize(horizontal: false, vertical: true) | ||||||
|                                     } |                                     } | ||||||
|                                     .frame(width: screenSize().width - 53.4, alignment: .leading) |                                     .frame(width: screenSize().width - 53.4, alignment: .leading) | ||||||
| @@ -209,11 +212,11 @@ struct CanPgPaymentView: View { | |||||||
|                                     HStack(alignment: .top, spacing: 0) { |                                     HStack(alignment: .top, spacing: 0) { | ||||||
|                                         Text("- ") |                                         Text("- ") | ||||||
|                                             .font(.custom(Font.medium.rawValue, size: 12)) |                                             .font(.custom(Font.medium.rawValue, size: 12)) | ||||||
|                                             .foregroundColor(Color(hex: "777777")) |                                             .foregroundColor(Color.gray77) | ||||||
|                                          |                                          | ||||||
|                                         Text("결제 취소는 결제 후 7일 이내에만 할 수 있습니다.\n단, 캔의 일부를 사용하면 결제 취소를 할 수 없습니다.") |                                         Text("결제 취소는 결제 후 7일 이내에만 할 수 있습니다.\n단, 캔의 일부를 사용하면 결제 취소를 할 수 없습니다.") | ||||||
|                                             .font(.custom(Font.medium.rawValue, size: 12)) |                                             .font(.custom(Font.medium.rawValue, size: 12)) | ||||||
|                                             .foregroundColor(Color(hex: "777777")) |                                             .foregroundColor(Color.gray77) | ||||||
|                                             .fixedSize(horizontal: false, vertical: true) |                                             .fixedSize(horizontal: false, vertical: true) | ||||||
|                                     } |                                     } | ||||||
|                                     .frame(width: screenSize().width - 53.4, alignment: .leading) |                                     .frame(width: screenSize().width - 53.4, alignment: .leading) | ||||||
| @@ -221,11 +224,11 @@ struct CanPgPaymentView: View { | |||||||
|                                     HStack(alignment: .top, spacing: 0) { |                                     HStack(alignment: .top, spacing: 0) { | ||||||
|                                         Text("- ") |                                         Text("- ") | ||||||
|                                             .font(.custom(Font.medium.rawValue, size: 12)) |                                             .font(.custom(Font.medium.rawValue, size: 12)) | ||||||
|                                             .foregroundColor(Color(hex: "777777")) |                                             .foregroundColor(Color.gray77) | ||||||
|                                          |                                          | ||||||
|                                         Text("광고성 이벤트 등 회사가 무료로 지급한 포인트는 환불되지 않습니다.") |                                         Text("광고성 이벤트 등 회사가 무료로 지급한 포인트는 환불되지 않습니다.") | ||||||
|                                             .font(.custom(Font.medium.rawValue, size: 12)) |                                             .font(.custom(Font.medium.rawValue, size: 12)) | ||||||
|                                             .foregroundColor(Color(hex: "777777")) |                                             .foregroundColor(Color.gray77) | ||||||
|                                             .fixedSize(horizontal: false, vertical: true) |                                             .fixedSize(horizontal: false, vertical: true) | ||||||
|                                     } |                                     } | ||||||
|                                     .frame(width: screenSize().width - 53.4, alignment: .leading) |                                     .frame(width: screenSize().width - 53.4, alignment: .leading) | ||||||
| @@ -233,11 +236,11 @@ struct CanPgPaymentView: View { | |||||||
|                                     HStack(alignment: .top, spacing: 0) { |                                     HStack(alignment: .top, spacing: 0) { | ||||||
|                                         Text("- ") |                                         Text("- ") | ||||||
|                                             .font(.custom(Font.medium.rawValue, size: 12)) |                                             .font(.custom(Font.medium.rawValue, size: 12)) | ||||||
|                                             .foregroundColor(Color(hex: "777777")) |                                             .foregroundColor(Color.gray77) | ||||||
|                                          |                                          | ||||||
|                                         Text("자세한 내용은 소다라이브 이용약관에서 확인할 수 있습니다.") |                                         Text("자세한 내용은 소다라이브 이용약관에서 확인할 수 있습니다.") | ||||||
|                                             .font(.custom(Font.medium.rawValue, size: 12)) |                                             .font(.custom(Font.medium.rawValue, size: 12)) | ||||||
|                                             .foregroundColor(Color(hex: "777777")) |                                             .foregroundColor(Color.gray77) | ||||||
|                                             .fixedSize(horizontal: false, vertical: true) |                                             .fixedSize(horizontal: false, vertical: true) | ||||||
|                                     } |                                     } | ||||||
|                                     .frame(width: screenSize().width - 53.4, alignment: .leading) |                                     .frame(width: screenSize().width - 53.4, alignment: .leading) | ||||||
| @@ -251,12 +254,12 @@ struct CanPgPaymentView: View { | |||||||
|                             VStack(alignment: .leading, spacing: 5) { |                             VStack(alignment: .leading, spacing: 5) { | ||||||
|                                 Text("결제금액") |                                 Text("결제금액") | ||||||
|                                     .font(.custom(Font.medium.rawValue, size: 13.3)) |                                     .font(.custom(Font.medium.rawValue, size: 13.3)) | ||||||
|                                     .foregroundColor(Color(hex: "eeeeee")) |                                     .foregroundColor(Color.grayee) | ||||||
|                                  |                                  | ||||||
|                                 HStack(spacing: 0) { |                                 HStack(spacing: 0) { | ||||||
|                                     Text("\(canResponse.price) 원") |                                     Text("\(canResponse.price) 원") | ||||||
|                                         .font(.custom(Font.bold.rawValue, size: 23.3)) |                                         .font(.custom(Font.bold.rawValue, size: 23.3)) | ||||||
|                                         .foregroundColor(Color(hex: "eeeeee")) |                                         .foregroundColor(Color.grayee) | ||||||
|                                 } |                                 } | ||||||
|                             } |                             } | ||||||
|                              |                              | ||||||
| @@ -267,7 +270,7 @@ struct CanPgPaymentView: View { | |||||||
|                                 .foregroundColor(.white) |                                 .foregroundColor(.white) | ||||||
|                                 .padding(.vertical, 16) |                                 .padding(.vertical, 16) | ||||||
|                                 .frame(minWidth: 200) |                                 .frame(minWidth: 200) | ||||||
|                                 .background(Color(hex: "9970ff")) |                                 .background(Color.button) | ||||||
|                                 .cornerRadius(10) |                                 .cornerRadius(10) | ||||||
|                                 .onTapGesture { |                                 .onTapGesture { | ||||||
|                                     if viewModel.paymentMethod == nil { |                                     if viewModel.paymentMethod == nil { | ||||||
| @@ -290,12 +293,12 @@ struct CanPgPaymentView: View { | |||||||
|                         .padding(.leading, 22) |                         .padding(.leading, 22) | ||||||
|                         .padding(.trailing, 13.3) |                         .padding(.trailing, 13.3) | ||||||
|                         .padding(.vertical, 13.3) |                         .padding(.vertical, 13.3) | ||||||
|                         .background(Color(hex: "222222")) |                         .background(Color.gray22) | ||||||
|                         .cornerRadius(16.7, corners: [.topLeft, .topRight]) |                         .cornerRadius(16.7, corners: [.topLeft, .topRight]) | ||||||
|                          |                          | ||||||
|                         if proxy.safeAreaInsets.bottom > 0 { |                         if proxy.safeAreaInsets.bottom > 0 { | ||||||
|                             Rectangle() |                             Rectangle() | ||||||
|                                 .foregroundColor(Color(hex: "222222")) |                                 .foregroundColor(Color.gray22) | ||||||
|                                 .frame(width: proxy.size.width, height: 15.3) |                                 .frame(width: proxy.size.width, height: 15.3) | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
| @@ -308,7 +311,7 @@ struct CanPgPaymentView: View { | |||||||
|                                     .padding(.horizontal, 6.7) |                                     .padding(.horizontal, 6.7) | ||||||
|                                     .frame(width: geo.size.width - 66.7, alignment: .center) |                                     .frame(width: geo.size.width - 66.7, alignment: .center) | ||||||
|                                     .font(.custom(Font.medium.rawValue, size: 12)) |                                     .font(.custom(Font.medium.rawValue, size: 12)) | ||||||
|                                     .background(Color(hex: "9970ff")) |                                     .background(Color.button) | ||||||
|                                     .foregroundColor(Color.white) |                                     .foregroundColor(Color.white) | ||||||
|                                     .multilineTextAlignment(.leading) |                                     .multilineTextAlignment(.leading) | ||||||
|                                     .fixedSize(horizontal: false, vertical: true) |                                     .fixedSize(horizontal: false, vertical: true) | ||||||
| @@ -326,25 +329,6 @@ struct CanPgPaymentView: View { | |||||||
|                 LoadingView() |                 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() |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,12 @@ | |||||||
|  | // | ||||||
|  | //  CanChargeTempRequest.swift | ||||||
|  | //  SodaLive | ||||||
|  | // | ||||||
|  | //  Created by klaus on 5/20/24. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | struct CanChargeTempRequest: Encodable { | ||||||
|  |     let can: Int | ||||||
|  |     let price: Int | ||||||
|  |     let paymentGateway: PaymentGateway | ||||||
|  | } | ||||||
| @@ -0,0 +1,23 @@ | |||||||
|  | // | ||||||
|  | //  CanPaymentTempRepository.swift | ||||||
|  | //  SodaLive | ||||||
|  | // | ||||||
|  | //  Created by klaus on 5/20/24. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | import Foundation | ||||||
|  | import CombineMoya | ||||||
|  | import Combine | ||||||
|  | import Moya | ||||||
|  |  | ||||||
|  | class CanPaymentTempRepository { | ||||||
|  |     private let api = MoyaProvider<CanTempApi>() | ||||||
|  |      | ||||||
|  |     func chargeCan(request: CanChargeTempRequest) -> AnyPublisher<Response, MoyaError> { | ||||||
|  |         return api.requestPublisher(.chargeCan(request: request)) | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     func pgVerify(receiptId: String, orderId: String) -> AnyPublisher<Response, MoyaError> { | ||||||
|  |         return api.requestPublisher(.verify(request: PgVerifyRequest(receiptId: receiptId, orderId: orderId))) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,275 @@ | |||||||
|  | // | ||||||
|  | //  CanPaymentTempView.swift | ||||||
|  | //  SodaLive | ||||||
|  | // | ||||||
|  | //  Created by klaus on 5/20/24. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | import SwiftUI | ||||||
|  | import Bootpay | ||||||
|  | import BootpayUI | ||||||
|  |  | ||||||
|  | struct CanPaymentTempView: View { | ||||||
|  |      | ||||||
|  |     @StateObject var viewModel = CanPaymentTempViewModel() | ||||||
|  |      | ||||||
|  |     let title: String | ||||||
|  |     let can: Int | ||||||
|  |     let onSuccess: () -> Void | ||||||
|  |      | ||||||
|  |     init(title: String, can: Int, onSuccess: @escaping () -> Void) { | ||||||
|  |         self.title = title | ||||||
|  |         self.can = can | ||||||
|  |         self.onSuccess = onSuccess | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     var body: some View { | ||||||
|  |         ZStack { | ||||||
|  |             Color.black.ignoresSafeArea() | ||||||
|  |              | ||||||
|  |             if viewModel.isShowPaymentView { | ||||||
|  |                 BootpayUI(payload: viewModel.payload, requestType: BootpayRequest.TYPE_PAYMENT) | ||||||
|  |                     .onConfirm { | ||||||
|  |                         DEBUG_LOG("onConfirm: \($0)") | ||||||
|  |                         return true | ||||||
|  |                     } | ||||||
|  |                     .onCancel { | ||||||
|  |                         DEBUG_LOG("onCancel: \($0)") | ||||||
|  |                     } | ||||||
|  |                     .onError { | ||||||
|  |                         DEBUG_LOG("onError: \($0)") | ||||||
|  |                         viewModel.isShowPaymentView = false | ||||||
|  |                         viewModel.errorMessage = "결제 중 오류가 발생했습니다." | ||||||
|  |                         viewModel.isShowPopup = true | ||||||
|  |                     } | ||||||
|  |                     .onDone { | ||||||
|  |                         DEBUG_LOG("onDone: \($0)") | ||||||
|  |                         viewModel.verifyPayment($0) { | ||||||
|  |                             let can = UserDefaults.int(forKey: .can) | ||||||
|  |                             UserDefaults.set(can + self.can, forKey: .can) | ||||||
|  |                              | ||||||
|  |                             DispatchQueue.main.asyncAfter(deadline: .now() + 1) { | ||||||
|  |                                 AppState.shared.back() | ||||||
|  |                                 onSuccess() | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     .onClose { | ||||||
|  |                         DEBUG_LOG("onClose") | ||||||
|  |                         viewModel.isShowPaymentView = false | ||||||
|  |                     } | ||||||
|  |             } else { | ||||||
|  |                 GeometryReader { proxy in | ||||||
|  |                     VStack(spacing: 0) { | ||||||
|  |                         DetailNavigationBar(title: "결제하기") | ||||||
|  |                          | ||||||
|  |                         ScrollView(.vertical, showsIndicators: false) { | ||||||
|  |                             VStack(spacing: 0) { | ||||||
|  |                                 HStack(spacing: 0) {                                     | ||||||
|  |                                     Text(self.title) | ||||||
|  |                                         .font(.custom(Font.bold.rawValue, size: 15.3)) | ||||||
|  |                                         .foregroundColor(Color(hex: "eeeeee")) | ||||||
|  |                                         .padding(.leading, 13.3) | ||||||
|  |                                      | ||||||
|  |                                     Spacer() | ||||||
|  |                                      | ||||||
|  |                                     Text("\(self.can * 110) 원") | ||||||
|  |                                         .font(.custom(Font.bold.rawValue, size: 15.3)) | ||||||
|  |                                         .foregroundColor(Color.grayee) | ||||||
|  |                                 } | ||||||
|  |                                 .padding(.horizontal, 13.3) | ||||||
|  |                                 .padding(.vertical, 23.3) | ||||||
|  |                                 .background(Color.gray22) | ||||||
|  |                                 .cornerRadius(16.7) | ||||||
|  |                                 .padding(.horizontal, 13.3) | ||||||
|  |                                 .frame(width: screenSize().width) | ||||||
|  |                                 .padding(.top, 13.3) | ||||||
|  |                                  | ||||||
|  |                                 Text("결제 수단 선택") | ||||||
|  |                                     .font(.custom(Font.bold.rawValue, size: 16.7)) | ||||||
|  |                                     .foregroundColor(Color.grayee) | ||||||
|  |                                     .frame(width: screenSize().width - 26.7, alignment: .leading) | ||||||
|  |                                     .padding(.top, 26.7) | ||||||
|  |                                  | ||||||
|  |                                  | ||||||
|  |                                 HStack(spacing: 13.3) { | ||||||
|  |                                     Text("카드") | ||||||
|  |                                         .font(.custom( viewModel.paymentMethod == .card ? Font.bold.rawValue : Font.medium.rawValue, size: 16.7)) | ||||||
|  |                                         .foregroundColor(viewModel.paymentMethod == .card ? Color.button : Color.grayee) | ||||||
|  |                                         .frame(width: (screenSize().width - 40) / 2) | ||||||
|  |                                         .padding(.vertical, 16.7) | ||||||
|  |                                         .background( | ||||||
|  |                                             viewModel.paymentMethod == .card ? | ||||||
|  |                                             Color.button.opacity(0.3) : | ||||||
|  |                                                 Color.gray23 | ||||||
|  |                                         ) | ||||||
|  |                                         .cornerRadius(10) | ||||||
|  |                                         .overlay( | ||||||
|  |                                             RoundedRectangle(cornerRadius: 10) | ||||||
|  |                                                 .stroke(lineWidth: 1) | ||||||
|  |                                                 .foregroundColor(viewModel.paymentMethod == .card ? Color.button : Color.gray77) | ||||||
|  |                                         ) | ||||||
|  |                                         .onTapGesture { | ||||||
|  |                                             if viewModel.paymentMethod != .card { | ||||||
|  |                                                 viewModel.paymentMethod = .card | ||||||
|  |                                             } | ||||||
|  |                                         } | ||||||
|  |                                      | ||||||
|  |                                      | ||||||
|  |                                     Text("계좌이체") | ||||||
|  |                                         .font(.custom( viewModel.paymentMethod == .bank ? Font.bold.rawValue : Font.medium.rawValue, size: 16.7)) | ||||||
|  |                                         .foregroundColor(viewModel.paymentMethod == .bank ? Color.button : Color.grayee) | ||||||
|  |                                         .frame(width: (screenSize().width - 40) / 2) | ||||||
|  |                                         .padding(.vertical, 16.7) | ||||||
|  |                                         .background( | ||||||
|  |                                             viewModel.paymentMethod == .bank ? | ||||||
|  |                                             Color.button.opacity(0.3) : | ||||||
|  |                                                 Color.gray23 | ||||||
|  |                                         ) | ||||||
|  |                                         .cornerRadius(10) | ||||||
|  |                                         .overlay( | ||||||
|  |                                             RoundedRectangle(cornerRadius: 10) | ||||||
|  |                                                 .stroke(lineWidth: 1) | ||||||
|  |                                                 .foregroundColor(viewModel.paymentMethod == .bank ? Color.button : Color.gray77) | ||||||
|  |                                         ) | ||||||
|  |                                         .onTapGesture { | ||||||
|  |                                             if viewModel.paymentMethod != .bank { | ||||||
|  |                                                 viewModel.paymentMethod = .bank | ||||||
|  |                                             } | ||||||
|  |                                         } | ||||||
|  |                                 } | ||||||
|  |                                 .frame(width: screenSize().width - 26.7) | ||||||
|  |                                 .padding(.top, 16.7) | ||||||
|  |                                  | ||||||
|  |                                 HStack(spacing: 13.3) { | ||||||
|  |                                     Text("휴대폰 결제") | ||||||
|  |                                         .font(.custom( viewModel.paymentMethod == .phone ? Font.bold.rawValue : Font.medium.rawValue, size: 16.7)) | ||||||
|  |                                         .foregroundColor(viewModel.paymentMethod == .phone ? Color.button : Color.grayee) | ||||||
|  |                                         .frame(width: (screenSize().width - 40) / 2) | ||||||
|  |                                         .padding(.vertical, 16.7) | ||||||
|  |                                         .background( | ||||||
|  |                                             viewModel.paymentMethod == .phone ? | ||||||
|  |                                             Color.button.opacity(0.3) : | ||||||
|  |                                                 Color.gray23 | ||||||
|  |                                         ) | ||||||
|  |                                         .cornerRadius(10) | ||||||
|  |                                         .overlay( | ||||||
|  |                                             RoundedRectangle(cornerRadius: 10) | ||||||
|  |                                                 .stroke(lineWidth: 1) | ||||||
|  |                                                 .foregroundColor(viewModel.paymentMethod == .phone ? Color.button : Color.gray77) | ||||||
|  |                                         ) | ||||||
|  |                                         .onTapGesture { | ||||||
|  |                                             if viewModel.paymentMethod != .phone { | ||||||
|  |                                                 viewModel.paymentMethod = .phone | ||||||
|  |                                             } | ||||||
|  |                                         } | ||||||
|  |                                      | ||||||
|  |                                     Spacer() | ||||||
|  |                                 } | ||||||
|  |                                 .frame(width: screenSize().width - 26.7) | ||||||
|  |                                 .padding(.top, 16.7) | ||||||
|  |                                  | ||||||
|  |                                 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.grayee) | ||||||
|  |                                 } | ||||||
|  |                                 .frame(width: screenSize().width - 53.4, alignment: .leading) | ||||||
|  |                                 .padding(.top, 16.7) | ||||||
|  |                                 .onTapGesture { | ||||||
|  |                                     viewModel.isTermsAgree.toggle() | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                          | ||||||
|  |                         Spacer() | ||||||
|  |                          | ||||||
|  |                         HStack(spacing: 0) { | ||||||
|  |                             VStack(alignment: .leading, spacing: 5) { | ||||||
|  |                                 Text("결제금액") | ||||||
|  |                                     .font(.custom(Font.medium.rawValue, size: 13.3)) | ||||||
|  |                                     .foregroundColor(Color.grayee) | ||||||
|  |                                  | ||||||
|  |                                 HStack(spacing: 0) { | ||||||
|  |                                     Text("\(self.can * 110) 원") | ||||||
|  |                                         .font(.custom(Font.bold.rawValue, size: 23.3)) | ||||||
|  |                                         .foregroundColor(Color.grayee) | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                              | ||||||
|  |                             Spacer() | ||||||
|  |                              | ||||||
|  |                             Text("결제하기") | ||||||
|  |                                 .font(.custom(Font.bold.rawValue, size: 18.3)) | ||||||
|  |                                 .foregroundColor(.white) | ||||||
|  |                                 .padding(.vertical, 16) | ||||||
|  |                                 .frame(minWidth: 200) | ||||||
|  |                                 .background(Color.button) | ||||||
|  |                                 .cornerRadius(10) | ||||||
|  |                                 .onTapGesture { | ||||||
|  |                                     if viewModel.paymentMethod == nil { | ||||||
|  |                                         viewModel.errorMessage = "결제수단을 선택해 주세요." | ||||||
|  |                                         viewModel.isShowPopup = true | ||||||
|  |                                     } else if !viewModel.isTermsAgree { | ||||||
|  |                                         viewModel.errorMessage = "결제진행에 동의하셔야 결제가 가능합니다." | ||||||
|  |                                         viewModel.isShowPopup = true | ||||||
|  |                                     } else { | ||||||
|  |                                         viewModel.chargeCan(can: can, paymentGateway: .PG){ | ||||||
|  |                                             viewModel.payload.orderName = self.title | ||||||
|  |                                             viewModel.payload.price = Double(self.can * 110) | ||||||
|  |                                             viewModel.payload.taxFree = 0 | ||||||
|  |                                              | ||||||
|  |                                             viewModel.isShowPaymentView = true | ||||||
|  |                                         } | ||||||
|  |                                     } | ||||||
|  |                                 } | ||||||
|  |                         } | ||||||
|  |                         .padding(.leading, 22) | ||||||
|  |                         .padding(.trailing, 13.3) | ||||||
|  |                         .padding(.vertical, 13.3) | ||||||
|  |                         .background(Color.gray22) | ||||||
|  |                         .cornerRadius(16.7, corners: [.topLeft, .topRight]) | ||||||
|  |                          | ||||||
|  |                         if proxy.safeAreaInsets.bottom > 0 { | ||||||
|  |                             Rectangle() | ||||||
|  |                                 .foregroundColor(Color.gray22) | ||||||
|  |                                 .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.button) | ||||||
|  |                                     .foregroundColor(Color.white) | ||||||
|  |                                     .multilineTextAlignment(.leading) | ||||||
|  |                                     .fixedSize(horizontal: false, vertical: true) | ||||||
|  |                                     .cornerRadius(20) | ||||||
|  |                                     .padding(.top, 66.7) | ||||||
|  |                                 Spacer() | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     .edgesIgnoringSafeArea(.bottom) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             if viewModel.isLoading { | ||||||
|  |                 LoadingView() | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #Preview { | ||||||
|  |     CanPaymentTempView(title: "콘텐츠 제목", can: 1000, onSuccess: {}) | ||||||
|  | } | ||||||
| @@ -0,0 +1,129 @@ | |||||||
|  | // | ||||||
|  | //  CanPaymentTempViewModel.swift | ||||||
|  | //  SodaLive | ||||||
|  | // | ||||||
|  | //  Created by klaus on 5/20/24. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | import Foundation | ||||||
|  | import Combine | ||||||
|  |  | ||||||
|  | import Bootpay | ||||||
|  |  | ||||||
|  | enum TempPaymentMethod: String { | ||||||
|  |     case card = "카드" | ||||||
|  |     case bank = "계좌이체" | ||||||
|  |     case phone = "휴대폰" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | final class CanPaymentTempViewModel: ObservableObject { | ||||||
|  |   | ||||||
|  |     private let repository = CanPaymentTempRepository() | ||||||
|  |     private var subscription = Set<AnyCancellable>() | ||||||
|  |      | ||||||
|  |     @Published var isTermsAgree = false | ||||||
|  |     @Published var errorMessage = "" | ||||||
|  |     @Published var isShowPopup = false | ||||||
|  |     @Published var isLoading = false | ||||||
|  |      | ||||||
|  |     @Published var isShowPaymentView = false | ||||||
|  |     @Published var paymentMethod: TempPaymentMethod? = nil | ||||||
|  |      | ||||||
|  |     let payload = Payload() | ||||||
|  |      | ||||||
|  |     func chargeCan(can: Int, paymentGateway: PaymentGateway, onSuccess: @escaping () -> Void) { | ||||||
|  |         isLoading = true | ||||||
|  |         repository.chargeCan(request: CanChargeTempRequest(can: can, price: can * 110, paymentGateway: paymentGateway)) | ||||||
|  |             .sink { result in | ||||||
|  |                 switch result { | ||||||
|  |                 case .finished: | ||||||
|  |                     DEBUG_LOG("finish") | ||||||
|  |                 case .failure(let error): | ||||||
|  |                     ERROR_LOG(error.localizedDescription) | ||||||
|  |                 } | ||||||
|  |             } receiveValue: { [unowned self] response in | ||||||
|  |                 self.isLoading = false | ||||||
|  |                 let responseData = response.data | ||||||
|  |                  | ||||||
|  |                 do { | ||||||
|  |                     let jsonDecoder = JSONDecoder() | ||||||
|  |                     let decoded = try jsonDecoder.decode(ApiResponse<CanChargeResponse>.self, from: responseData) | ||||||
|  |                      | ||||||
|  |                     if let data = decoded.data, decoded.success { | ||||||
|  |                         let bootUser = BootUser() | ||||||
|  |                         bootUser.userId = "\(UserDefaults.int(forKey: .userId))" | ||||||
|  |                         bootUser.username = UserDefaults.string(forKey: .nickname) | ||||||
|  |                          | ||||||
|  |                         payload.applicationId = BOOTPAY_APP_ID | ||||||
|  |                         payload.pg = "세틀뱅크" | ||||||
|  |                         payload.orderId = "\(data.chargeId)" | ||||||
|  |                         payload.method = paymentMethod!.rawValue | ||||||
|  |                         payload.user = bootUser | ||||||
|  |                          | ||||||
|  |                         onSuccess() | ||||||
|  |                     } else { | ||||||
|  |                         if let message = decoded.message { | ||||||
|  |                             self.errorMessage = message | ||||||
|  |                         } else { | ||||||
|  |                             self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." | ||||||
|  |                         } | ||||||
|  |                          | ||||||
|  |                         self.isShowPopup = true | ||||||
|  |                     } | ||||||
|  |                 } catch { | ||||||
|  |                     self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." | ||||||
|  |                     self.isShowPopup = true | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             .store(in: &subscription) | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     func verifyPayment(_ data: [String: Any], onSuccess: @escaping () -> Void) { | ||||||
|  |         isLoading = true | ||||||
|  |          | ||||||
|  |         let _data = data["data"] as? [String: Any] | ||||||
|  |          | ||||||
|  |         if let data = _data { | ||||||
|  |             let receiptId = data["receipt_id"] as! String | ||||||
|  |             let orderId = data["order_id"] as! String | ||||||
|  |              | ||||||
|  |             repository.pgVerify(receiptId: receiptId, orderId: orderId) | ||||||
|  |                 .sink { result in | ||||||
|  |                     switch result { | ||||||
|  |                     case .finished: | ||||||
|  |                         DEBUG_LOG("finish") | ||||||
|  |                     case .failure(let error): | ||||||
|  |                         ERROR_LOG(error.localizedDescription) | ||||||
|  |                     } | ||||||
|  |                 } receiveValue: { [unowned self] response in | ||||||
|  |                     self.isLoading = false | ||||||
|  |                     let responseData = response.data | ||||||
|  |  | ||||||
|  |                     do { | ||||||
|  |                         let jsonDecoder = JSONDecoder() | ||||||
|  |                         let decoded = try jsonDecoder.decode(ApiResponseWithoutData.self, from: responseData) | ||||||
|  |  | ||||||
|  |                         if decoded.success { | ||||||
|  |                             onSuccess() | ||||||
|  |                         } else { | ||||||
|  |                             if let message = decoded.message { | ||||||
|  |                                 self.errorMessage = message | ||||||
|  |                             } else { | ||||||
|  |                                 self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." | ||||||
|  |                             } | ||||||
|  |  | ||||||
|  |                             self.isShowPopup = true | ||||||
|  |                         } | ||||||
|  |                     } catch { | ||||||
|  |                         self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." | ||||||
|  |                         self.isShowPopup = true | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 .store(in: &subscription) | ||||||
|  |         } else { | ||||||
|  |             isLoading = false | ||||||
|  |             errorMessage = "본인인증 중 오류가 발생했습니다." | ||||||
|  |             isShowPopup = true | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										55
									
								
								SodaLive/Sources/MyPage/Can/Payment/Temp/CanTempApi.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								SodaLive/Sources/MyPage/Can/Payment/Temp/CanTempApi.swift
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | |||||||
|  | // | ||||||
|  | //  CanTempApi.swift | ||||||
|  | //  SodaLive | ||||||
|  | // | ||||||
|  | //  Created by klaus on 5/20/24. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | import Foundation | ||||||
|  | import Moya | ||||||
|  |  | ||||||
|  | enum CanTempApi { | ||||||
|  |     case chargeCan(request: CanChargeTempRequest) | ||||||
|  |     case verify(request: PgVerifyRequest) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | extension CanTempApi: TargetType { | ||||||
|  |     var baseURL: URL { | ||||||
|  |         return URL(string: BASE_URL)! | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     var path: String { | ||||||
|  |         switch self { | ||||||
|  |         case .chargeCan: | ||||||
|  |             return "/charge/temp" | ||||||
|  |              | ||||||
|  |         case .verify: | ||||||
|  |             return "/charge/temp/verify" | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     var method: Moya.Method { | ||||||
|  |         switch self { | ||||||
|  |         case .chargeCan, .verify: | ||||||
|  |             return .post | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     var task: Task { | ||||||
|  |         switch self { | ||||||
|  |         case .chargeCan(let request): | ||||||
|  |             return .requestJSONEncodable(request) | ||||||
|  |              | ||||||
|  |         case .verify(let request): | ||||||
|  |             return .requestJSONEncodable(request) | ||||||
|  |              | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     var headers: [String : String]? { | ||||||
|  |         switch self { | ||||||
|  |         default: | ||||||
|  |             return ["Authorization": "Bearer \(UserDefaults.string(forKey: UserDefaultsKey.token))"] | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 Yu Sung
					Yu Sung