Development/React Native
[React Native] 토스 결제위젯, 브랜드페이 SDK 연동하기
레오나르도 다빈츠
2024. 8. 3. 19:17
담당자에게 Version 2는 React Native를 지원하지 않는다는 답변을 받은 후, Version 1으로 진행하게 되었다. 토스 문서(https://docs.tosspayments.com/sdk/widget-rn)에 링크되어있는 샘플 프로젝트로 간단히 연동할 수 있었다.
1. 설치하기
공식 문서에는 나와있지 않지만 tosspayments-react-native-webview도 함께 설치해야 한다. 자꾸 빌드가 안돼서 샘플 프로젝트랑 비교하면서 찾았다.
npm install @tosspayments/widget-sdk-react-native
npm install tosspayments-react-native-webview
2. 구현
NavigationContainer 안에서 <PaymentWidgetProvider>로 감싸주고 PaymentWidgetProvider에 clientKey(어드민에서 설정)와 customerKey(규격에 맞게 직접 발급)를 설정한다.
<NavigationContainer>
<PaymentWidgetProvider clientKey="CLIENT_KEY" customerKey="CUSTOMER_KEY">
<Stack.Navigator>
// ....
</Stack.Navigator>
</TossPaymentControlProvider>
</NavigationContainer>
토스 어드민에서 설정한 커스텀 UI의 varientKey를 넣어준다.
import React, { useState } from 'react'
import { usePaymentWidget, AgreementWidget, PaymentMethodWidget } from '@tosspayments/widget-sdk-react-native'
import { Alert, Button } from 'react-native'
import type { AgreementWidgetControl, PaymentMethodWidgetControl } from '@tosspayments/widget-sdk-react-native'
export default function ExPaymentWidget() {
const paymentWidgetControl = usePaymentWidget()
const [paymentMethodWidgetControl, setPaymentMethodWidgetControl] = useState<PaymentMethodWidgetControl | null>(null)
const [agreementWidgetControl, setAgreementWidgetControl] = useState<AgreementWidgetControl | null>(null)
return (
<>
<PaymentMethodWidget
selector="payment-methods"
onLoadEnd={() => {
paymentWidgetControl
.renderPaymentMethods(
'payment-methods',
{ value: 50000 },
{
variantKey: 'DEFAULT', // 토스 어드민 > UI 설정값
},
)
.then((control) => {
setPaymentMethodWidgetControl(control)
})
}}
/>
<AgreementWidget
selector="agreement"
onLoadEnd={() => {
paymentWidgetControl
.renderAgreement('agreement', {
variantKey: 'DEFAULT',
})
.then((control) => {
setAgreementWidgetControl(control)
})
}}
/>
<Button
title="결제요청"
onPress={async () => {
if (paymentWidgetControl == null || agreementWidgetControl == null) {
Alert.alert('주문 정보가 초기화되지 않았습니다.')
return
}
const agreeement = await agreementWidgetControl.getAgreementStatus()
if (agreeement.agreedRequiredTerms !== true) {
Alert.alert('약관에 동의하지 않았습니다.')
return
}
paymentWidgetControl
.requestPayment?.({
orderId: 'xucuO-uVCnioK2V3hSBIr',
orderName: '토스 티셔츠 외 2건',
})
.then((result) => {
if (result?.success) {
// 결제 성공 비즈니스 로직을 구현하세요.
// result.success에 있는 값을 서버로 전달해서 결제 승인을 호출하세요.
} else if (result?.fail) {
// 결제 실패 비즈니스 로직을 구현하세요.
}
})
}}
/>
<Button
title="선택된 결제수단"
onPress={async () => {
if (paymentMethodWidgetControl == null) {
Alert.alert('주문 정보가 초기화되지 않았습니다.')
return
}
Alert.alert(`선택된 결제수단: ${JSON.stringify(await paymentMethodWidgetControl.getSelectedPaymentMethod())}`)
}}
/>
<Button
title="결제 금액 변경"
onPress={() => {
if (paymentMethodWidgetControl == null) {
Alert.alert('주문 정보가 초기화되지 않았습니다.')
return
}
paymentMethodWidgetControl.updateAmount(100_000).then(() => {
Alert.alert('결제 금액이 100000원으로 변경되었습니다.')
})
}}
/>
</>
)
}
💥 트러블슈팅
1. 'Widget이 이미 존재한다'
widget은 2개 이상을 띄울 수 없다.
2. 결제 비밀번호 입력 화면으로 넘어가지 않고 종료되는 현상
- 신용카드: 100원 이상
- 계좌: 200원 이상
을 결제 금액으로 넘겨야 한다.
<PaymentMethodWidget
selector="payment-methods"
onLoadEnd={() => {
paymentWidgetControl
.renderPaymentMethods(
'payment-methods',
{ value: 0 }, // 0원으로 넘길 수 없다.
{
variantKey: 'CUSTOM_UI_KEY',
},
)
.then((control) => {
setPaymentMethodWidgetControl(control)
})
}}
/>