👽
Next.jsでGMO PAYMENT GATEWAYのトークン決済を実装する
参考にさせていただいた記事
環境
- next:12.0.7
- typescript:4.5.4
適当にwindowを拡張する😎
resultCode
やcreditCard
の返り値がイマイチなので、必要最低限の型(安全ではない)のみ
今回はresultCode
の返り値が'000'
以外の場合は問答無用でエラーにするのでtokenObject
のオプショナルをなくしている😂
global.d.ts
declare global {
interface Window {
Multipayment: {
getToken: (creditCard: {
cardno: number
expire: number // YYYYMM or YYMM
holdername: string
securitycode: number
}) => {
resultCode: `000` | number
tokenObject: {
isSecurityCodeSet: boolean | string
maskedCardNo: string
toBeExpiredAt: string
token: string
}
}
init: (apiKey: string) => void
}
}
}
const Multipayment = window.Multipayment
Next.jsのScriptを使いトークン取得のためのJavaScriptを読み込ませる👽
ステージングと本番でJavaScriptのURLが違うのでprocess.env.NEXT_PUBLIC_GMO_TOKEN_URL
に切り出す
onLoad
を使いJavaScriptが読み込まれたらMultipayment.init
を呼び出す
ちなみにafterInteractive
は初期値なのでつけなくても良い😎
GMOMultiPaymentScript.tsx
import Script from 'next/script'
/**
* @see https://nextjs.org/docs/basic-features/script
*/
type GMOMultiPaymentScriptProps = Required<
Readonly<{
apiKey: string
}>
>
export const GMOMultiPaymentScript = ({
apiKey,
}: GMOMultiPaymentScriptProps) => (
<Script
src={process.env.NEXT_PUBLIC_GMO_TOKEN_URL}
strategy={'afterInteractive'}
onLoad={() => {
Multipayment.init(apiKey)
}}
/>
)
トークンを取得するhooksを作成する✨
Awaited
はいいぞ!
useGMOMultiPayment.ts
export const useGMOMultiPayment = () => {
const getToken = (
creditCard: Parameters<typeof Multipayment.getToken>[0]
) => {
return new Promise<ReturnType<typeof Multipayment.getToken>>((resolve) => {
Multipayment.getToken(creditCard, resolve)
})
}
return {
getToken,
}
}
type useGMOMultiPaymentType = typeof useGMOMultiPayment
export type useGMOMultiPaymentResult = ReturnType<useGMOMultiPaymentType>
export type useGMOMultiPaymentGetTokenResult = Awaited<
ReturnType<useGMOMultiPaymentResult['getToken']>
>
とりあえず、雑な実装
テスト専用のカード
UIはchakra-uiをformはreact-hook-formを使っています。
import {
useGMOMultiPayment,
useGMOMultiPaymentGetTokenResult,
} from '@src/models/Payment/useGMOMultiPayment'
import { useForm } from 'react-hook-form'
export type GMOMultiPaymentInput = {
cardno: number
expireMonth: number
expireYear: number
holdername: string
securitycode: number
}
export const useCreatePaymentMethod = ({
onCompleted,
onError,
}: Partial<{
onCompleted: (data: useGMOMultiPaymentGetTokenResult) => void
onError: () => void
}> = {}) => {
const form = useForm<GMOMultiPaymentInput>()
const { getToken } = useGMOMultiPayment()
const onCreatePaymentMethod = form.handleSubmit(
async ({ cardno, expireMonth, expireYear, holdername, securitycode }) => {
const paddingMonth = String(expireMonth).padStart(2, '0')
try {
const data = await getToken({
cardno,
expire: `${expireYear}${paddingMonth}`,
holdername,
securitycode,
})
if (data.resultCode === '000') {
console.log(data.tokenObject.token)
onCompleted?.(data)
} else {
console.log('error')
onError?.()
}
} catch (e) {
console.log('error')
onError?.()
}
}
)
return {
form,
onCreatePaymentMethod,
}
}
type useCreatePaymentMethodType = typeof useCreatePaymentMethod
export type useCreatePaymentMethodResult = ReturnType<useCreatePaymentMethodType>
// export type useCreatePaymentMethodParameter = Parameters<useCreatePaymentMethodType>[0]
import {
Button,
chakra,
Flex,
FormControl,
FormLabel,
Input,
} from '@chakra-ui/react'
import {
GMOMultiPaymentScript,
useCreatePaymentMethod,
} from '@src/models/Payment'
export const CreatePaymentMethodForm = () => {
const { form, onCreatePaymentMethod } = useCreatePaymentMethod()
return (
<chakra.form
borderWidth={1}
maxW={600}
mx={'auto'}
px={4}
py={5}
onSubmit={onCreatePaymentMethod}
>
<GMOMultiPaymentScript apiKey={'your api key'} />
<FormControl>
<FormLabel>カード番号</FormLabel>
<Input
type={'number'}
{...form.register('cardno', { valueAsNumber: true })}
/>
</FormControl>
<Flex gap={2}>
<FormControl>
<FormLabel>年</FormLabel>
<Input
type={'number'}
{...form.register('expireYear', { valueAsNumber: true })}
/>
</FormControl>
<FormControl>
<FormLabel>月</FormLabel>
<Input
type={'number'}
{...form.register('expireMonth', { valueAsNumber: true })}
/>
</FormControl>
</Flex>
<FormControl>
<FormLabel>名義人</FormLabel>
<Input {...form.register('holdername')} />
</FormControl>
<FormControl>
<FormLabel>セキュリティコード</FormLabel>
<Input
maxW={'6em'}
textAlign={'right'}
type={'number'}
{...form.register('securitycode', { valueAsNumber: true })}
/>
</FormControl>
<Flex justify={'center'} mt={3}>
<Button
colorScheme={'teal'}
minW={'10em'}
type={'submit'}
variant={'outline'}
>
送信
</Button>
</Flex>
</chakra.form>
)
}
下記の画像のようなレスポンスが返ってきてたら成功!
お疲れ様でした☕️
Discussion