[iOS] - 부트페이 결제 연동하기

sun02·2023년 7월 27일
0

iOS

목록 보기
24/27
post-thumbnail

부트페이 결제 라이브러리를 사용하고 느낀점과 배운점을 기록합니다.

📍 나이스 페이먼트를 사용한 카드 정기결제, 통합결제를 다룹니다.

목차

  1. 라이브러리 적용 및 오류 해결
  2. Bootpay 사용하기
  3. 응답 Data parsing
  4. 느낀점

1. 라이브러리 적용하기

Bootpay 와 SwiftyBooty 중 'Bootpay' 사용

부트페이 홈페이지에서는 두 가지 라이브러리를 다루고 있지만 부트페이 깃헙의 가장 최근 예제에서 Bootpay를 사용하고 있어 Bootpay를 적용했습니다.


- Pod install

pod 'Bootpay'

해줍니다..
하면 에러가 되게되게 많이 나는데요

이 에러가 가장 많이 나옵니다.
iOS deployment target이 낮으니 높이라는 에러입니다.

근데, 저는 보통 iOS deployment target을 15.0 으로 하기 때문에 당최 왜 저런 에러가 나는 것인가 이해할 수가 없었습니다.


짜잔

앱의 iOS deployment target이 아닌 해당 라이브러리의 iOS deployment target을 말하는 것이였습니다

그래서 Bootpay 라이브러리의 Minimum target을 올려주고 Bootpay 하위 라이브러리인 CryptoSwift와 ObjectMapper도 Minimum target을 같이 올려주면 해결됩니다


‼️ pod에 새로운 라이브러리를 추가하여 pod install할 때마다
iOS deployment target이 초기 세팅값으로 돌아가니 그 때마다 다시 minimum target을 올려주어야합니다.


2. Bootpay 사용하기

Info.plist 외엔 따로 설정할 게 없습니다.


- Info.plist 설정

App Transport Security Settings -> Allow Arbitrary Loads를
"YES" 로 설정해줍니다.

타 카드사 앱으로 이동하는 것과 같이
다른 앱으로 이동했다가 다시 기존 앱으로 돌아와야하는 경우 CFBundleURLNameCFBundleURLSchemes도 설정해주어야합니다.

제가 진행 중인 앱은 해당 사항이 없어 설정하지 않았습니다.

- Bootpay 예제

일단, 어떻게 쓰는건지가 제일 관건인데요.
참고하기 가장 좋은 예제는 부트페이 깃헙에 있습니다.
저는 iOS-example-swift repo에 있는 예제가 제일 좋았습니다.

왜냐하면 각 케이스별 VC가 다 있습니다.

  • PG일반 - DefaultPaymentController
  • 통합결제 - TotalPaymentController
  • 카드자동 결제 (인증) - SubscriptionController
  • 카드자동 결제 (비인증) - SubscriptionBootpayController
  • 본인인증 - AuthenticationController
  • 생체인증 - BioController
  • 비밀번호 결제 - BioController
  • 웹앱으로 연동 - WebAppController

이렇게 참고하시면 되고, 저는 통합결제와 카드자동 결제만 참고했습니다

- 코드 작성하기

1. import

import Bootpay

2. bootpayStart() 호출

purchaseButton.rx.tap
			.subscribe(onNext: { [weak self] in
                self?.bootpayStart()
            })
            .disposed(by: disposeBag)

3. bootpayStart() 도입

func bootpayStart() {
		let payload = generatePayload()
        
        ...
}

4. payload 생성

func generatePayload() -> Payload {
        
        let payload = Payload()
        
        guard let membership = selectedPayment,
              let appUser = AppData.user else {
            return payload
        }
        
        payload.applicationId = BootpayManager.shared.appID
        
        if membership != .lifetime {
            payload.pg = "나이스페이"
            payload.method = "카드자동"
        }
        
        payload.price = membership.cost
        payload.orderId = membership.subscriptionId
        payload.subscriptionId = membership.subscriptionId
        payload.orderName = membership.bootPayTitle
        let extra = BootExtra()
        extra.cardQuota = String(3)
        payload.extra = extra
    
        let user = BootUser()
        user.username = appUser.name
        user.userId = appUser.uid
        payload.user = user
        return payload
}
  • payload.applicationId : 부트페이에서 제공하는 appID
  • payload.pg: 결제 요청할 수단 (ex 나이스페이, 다날 등등..)
  • payload.method: 결제 수단
  • payload.price: 결제 가격
  • payload.orderId, payload.subscriptionId: 상품 Id
  • extra: 추가 설정 (optional)
    + cardQuota: 할부
  • user: 사용자 정보 설정 (optional)

통합결제일 경우 payload.pg 와 payload.method를 비워둡니다.

작성할 때 각 pg사와 method의 string값을 모르는 경우 여기서 일반결제 요청하기 항목으로 가면

이렇게 각 파라미터 별 string 값을 확인할 수 있습니다.

5. bootpayStart()

이제 생성한 Payload를 파라미터로 전달하며 bootpay를 request 하면 끝입니다.

간단하게 보면 이렇게 생긴 request 입니다.

  • viewController: 부트페이 결제 모듈을 띄우고 싶은 VC
  • payload: 생성한 payload
  • isModal: 모달로 띄울건지
  • animation: 부트페이 띄울 때 애니메이션 줄건지
  • modalPresentationStyle: 모달로 띄우는 경우 어떻게 보여질건지

isModal, animation, modalPresentationStyle은 default 값이 설정되어 있어 request를 작성해도 보여지지 않을 수 있습니다.

default 값과 다르게 설정하고 싶다면 그냥 쑤셔서 적어버리면 됩니다.


 func bootpayStart() {
        let payload = generatePayload()
        
        if payload.method == "카드자동" {
            Bootpay.requestSubscription(viewController: self, payload: payload, isModal: true)
                .onCancel { data in
                    print("-- cancel: \(data)")
                }
                .onIssued { data in
                    print("-- issued: \(data)")
                }
                .onConfirm { data in
                    print("-- confirm: \(data)")
                    return true
                }
                .onDone { data in
                    self.viewModel?.input.addPaymentData.onNext(data)
                }
                .onError { data in
                    print("-- error: \(data)")
                }
                .onClose {
                    print("-- close")
                }
        } else {
            
            Bootpay.requestPayment(viewController: self, payload: payload, isModal: false)
                .onCancel { data in
                    print("-- cancel: \(data)")
                }
                .onIssued { data in
                    print("-- issued: \(data)")
                }
                .onConfirm { data in
                    print("-- confirm: \(data)")
                    return true
                }
                .onDone { data in
                    self.viewModel?.input.addPaymentData.onNext(data)
                }
                .onError { data in
                    print("-- error: \(data)")
                }
                .onClose {
                    print("-- close")
                }
        }
    }
  • Bootpay.requestSubscription : 카드 자동 결제 요청
  • Bootpay.requestPayment : 통합 결제 요청

저는 카드 자동 결제, 통합결제를 한 번에 처리하고자 이렇게 작성했습니다.

이 request는 각 상태 별 method를 제공하고 있습니다.
사용자가 결제를 완료한 경우 onDone이 호출되고 관련 data 를 전달받을 수 있습니다.

3. Data parsing

일단 onDone에서 전달받는 data는 정말 많은 데이터를 담고 있는데요.
JsonParser 를 사용해서 전달 받은 데이터 JSON 을 확인하면 파악하는데에 도움이 됩니다.

- Data 예시

‼️ 원본 데이터가 아닙니다 민감 데이터(ex card, 이름) 항목은 삭제했습니다. 대략적으로 이렇게 옵니다.


- 파싱하기

func parsePaymentData(_ data: [String: Any]) {
        if let dataDict = data["data"] as? [String: Any] {
            if let purchasedAt = dataDict["published_at"] as? String,
               let receiptId = dataDict["receipt_id"] as? String {
                
                getBillingKey(receiptId, purchasedAt)
                
            } else {
                print("Failed to retrieve the required data.")
            }
        } else {
            print("Data key not found in the dictionary.")
        }
    }

위처럼 필요한 항목만 골라서 사용했습니다.

  • data 파라미터에 전달받은 data 가공 없이 바로 넣으면 됩니다.

이렇게하면 결제는 끝입니다 !


다만 정기결제를 구현하는 경우 Billing_key를 발급받는 로직이 추가로 구현되어야합니다.

앱 단에서는 보안 상의 이유로 바로 Billing_key를 발급 받을 수 없고 서버에서 구현되어야합니다.

아직 하는 중이라, 이거도 하게되면 적어보겠습니다..


4. 사용하며 느낀 점

- 결제 모듈 UI가 예쁘다

  • 깔끔하고 iOS 랑 잘어울려서 좋았다

- 다양한 결제 수단을 통합적으로 간편하게 다룰 수 있어서 좋았다

  • 진짜 큰 장점 !!

- cocoapods 만 지원하는거 불편하다

  • 다른 라이브러리들 다 SPM으로 관리하고 있었는데 오로지 부트페이만을 위해 podFile을 설치해야했다 🥹
  • 처음 부트페이 넣었을 때 충돌 무지하게 나서 살짝 아찔

- pg, method를 string으로 작성해야하는게 불편하다

  • SwiftyBootpay에서는 객체로 제공했던데 왜..

- 개발 문서 너무 많아서 오히려 이해하기 어려웠다

  • 예제 코드들이 정리될 필요가 있어 보인다..ㅠㅠ

- 응답이 빠르시다

  • 홈페이지에 채널톡이 있어서 문의를 남겼었는데 빠르게 답변을 주셔서 작업 진행에 도움이 많이 되었다 👍🏻

1개의 댓글

comment-user-thumbnail
2023년 7월 27일

감사합니다. 이런 정보를 나눠주셔서 좋아요.

답글 달기