メインコンテンツまでスキップ

ECのAPIでカード決済を行うには?

KurocoのECモジュールのAPIでカード決済するにはカード番号などの情報を APIに直接渡すのでは無く、決済サービス会社の用意したAPIを利用し カード番号などを元にカードトークン情報を取得し、そのトークン情報を KurocoのAPIに設定する必要があります。

カード利用方法

Kurocoでサポートしている決済サービスはPaygentなので Paygentからカードトークンを取得するためのサンプルコードを記載します。 尚、詳細に関してはPaygent管理画面の「マニュアル/仕様書」より
02_PG外部インターフェース仕様説明書(トークン決済).pdf
をダウンロードして確認してください。

.envファイル設定

キー名
PAYGENT_MARCHANT_IDPaygentの「マーチャントID」
PAYGENT_TOKEN_GENERATE_PUBKEYPaygentの「トークン生成鍵」
PAYGENT_TOKEN_JS開発用)
https://sandbox.paygent.co.jp/js/PaygentToken.js
本番用)
https://token.paygent.co.jp/js/PaygentToken.js

fetched from Gyazo

サンプルコード

/components/CreditCardForm.vue
<template>
<div>
<vue-form-generator
ref="form"
:schema="schema"
:model="cardData"
class="c-form"
@model-updated="onInput"
/>
<div>
<button
type="button"
@click="subscribe()"
>
決済実行
</button>
</div>
</div>
</template>

<script>
import PaygentHelper from '@/util/paygentHelper';

export default Vue.extend({
name: 'CreditCardForm',
components: {
'vue-form-generator': VueFormGenerator.component
},
data() {
return {
paygent: null,
cardData: {
cardNumber: '',
expireMonth: '',
expireYear: '',
cvc: '',
name: '',
},
cardToken: '',
};
},
created() {
this.paygent = new PaygentHelper();
this.cardData.expireYear = this.formatYear(new Date().getFullYear() + 1);
},
mounted() {
this.schema = {
fields: [
{
type: 'vuetifyText',
inputType: 'text',
min: 0,
max: 16,
label: 'カード番号',
model: 'cardNumber',
text: this.cardData.cardNumber,
placeholder: '※ハイフンは入力しないでください。',
required: true,
texttype: 'number',
labelClasses: 'required',
},
{
label: '有効期限',
model: 'expireMonth',
placeholder: '月',
contents: [
{
key: '01',
value: '1月',
},
()
{
key: '12',
value: '12月',
},
],
option: {
key: this.cardData.expireMonth,
value: this.cardData.expireMonth + '月',
},
required: true,
labelClasses: 'required',
type: 'vuetifySingleOption',
styleClasses: 'c-form__twoColumns'
},
{
model: 'expireYear',
label: '',
placeholder: '年',
contents: [],
option: {
key: this.cardData.expireYear,
value: '20' + this.cardData.expireYear + '年',
},
required: true,
type: 'vuetifySingleOption',
styleClasses: 'c-form__twoColumns'
},
{
model: 'cvc',
type: 'vuetifyText',
inputType: 'text',
min: 3,
max: 4,
label: 'セキュリティコード',
text: this.cardData.cvc,
placeholder: '',
required: true,
labelClasses: 'required',
},
{
type: 'vuetifyText',
inputType: 'text',
min: 0,
max: 100,
label: 'カード名義人',
model: 'name',
text: this.cardData.name,
placeholder: '',
required: true,
labelClasses: 'required',
},
]
}

// 年を自動設定
this.schema.fields.map((item) => {
if (item.model === 'expireYear') {
const year_array = this.arrYear()
year_array.map((y) => {
let option = {
key: y,
value: '20' + y + '年'
}
item.contents.push(option)
})
}
return item
})
},
computed: {
canGenerateToken() {
const paygentConfigValues = Object.values(this.paygentConfig);
const cardDataValues = Object.values(this.cardData);
return (
paygentConfigValues.filter((v) => v).length === paygentConfigValues.length &&
cardDataValues.filter((v) => v).length === cardDataValues.length
);
},
},
methods: {
onInput (value, fieldName) {
this.$set(this.cardData, fieldName, value);
},
formatYear(year) {
return ('' + year).slice(-2);
},
arrYear() {
const date = new Date();
const thisYear = this.formatYear(date.getFullYear());
const intYear = parseInt(thisYear);
const years = [];
for (let y = intYear; y <= intYear + 10; y++) {
years.push(`${y}`);
}
return years;
},
async generateToken() {
this.cardToken = '';
try {
const response = await this.paygent.fetchToken(this.cardData);
this.cardToken = response.cardToken;
} catch (errorResponse) {
this.$store.dispatch('snackbar/setError', `[${errorResponse.code}] ${errorResponse.error}`);
this.$store.dispatch('snackbar/snackOn');
}
},
async subscribe() {
await this.generateToken()

if (this.cardToken === '') {
// トークン取得エラー
return;
}

const self = this;

this.loading = true

const cartItem = {
"product_id": 41202,
"quantity": 1
}
let orderInfo = {
"order_products": [
cartItem
],
"ec_payment_id": 58,
"card_token": self.cardToken,
}

this.$auth.ctx.$axios
.post('/rcms-api/1/subscribe', orderInfo)
.then(function (response) {
alert('購入しました。')
})
.catch(function (error) {
if (error.response) {
alert(error.response.data.errors?.[0].message);
} else {
alert("エラーが発生しました");
}
});
}
},
});
</script>
paygentHelper.js
import { paygentConfig, paygentScriptUrl, paygentErrorCodeDetails } from './paygentConfig';

export default class PaygentHelper {
constructor(config = null, lang = 'ja') {
this.config = config ? config : paygentConfig
this.lang = lang;

if (!document.body.querySelector(`script[src*='${paygentScriptUrl}']`)) {
// JS未読み込み
const script = document.createElement('script');
script.type = 'text/javascript';
script.src = paygentScriptUrl;
document.body.appendChild(script);
this.script = script;
this.script.onload = () => {
this.paygentToken = new PaygentToken();
};
} else {
// JS読み込み済み
this.paygentToken = new PaygentToken();
}
}
setConfig(config) {
this.config = config;
}
async fetchToken(cardData) {
return new Promise((resolve, reject) => {
if (!this.paygentToken) {
reject({
code: 'XXXX',
cardToken: '',
error: 'Unexpected error',
});
}
this.paygentToken.createToken(
this.config.merchantId,
this.config.tokenGeneratePubkey,
{
card_number: cardData.cardNumber,
expire_year: cardData.expireYear,
expire_month: cardData.expireMonth,
cvc: cardData.cvc,
name: cardData.name,
},
(response) => {
const resultCode = response.result || '';
if (response.result === undefined || response.result !== '0000') {
reject({
code: resultCode,
cardToken: '',
error: paygentErrorCodeDetails[resultCode]['ja'] || 'Unexpected error',
});
} else {
resolve({
code: resultCode,
cardToken: response.tokenizedCardObject.token,
error: '',
});
}
},
);
});
}
}
export const paygentConfig = {
merchantId: process.env.PAYGENT_MARCHANT_ID,
tokenGeneratePubkey: process.env.PAYGENT_TOKEN_GENERATE_PUBKEY,
};

export const paygentScriptUrl = process.env.PAYGENT_TOKEN_JS;
export const paygentErrorCodeDetails = {
'1100': {
en: 'Merchant id - Required',
ja: 'マーチャントID - 必須エラー',
},
'1200': {
en: 'Token generation pubkey - Required',
ja: 'トークン生成公開鍵 - 必須エラー',
},
'1201': {
en: 'Token generation pubkey - Invalid value',
ja: 'トークン生成公開鍵 - 不正エラー',
},
'1300': {
en: 'Card number - Required',
ja: 'カード番号 - 必須チェックエラー',
},
'1301': {
en: 'Card number - Invalid format',
ja: 'カード番号 - 書式チェックエラー',
},
'1400': {
en: 'Expiration year - Required',
ja: '有効期限(年) - 必須チェックエラー',
},
'1401': {
en: 'Expiration month - Invalid format',
ja: '書式チェックエラー - 数字以外が含まれている',
},
'1500': {
en: 'Expiration month - Required',
ja: '有効期限(月) - 必須チェックエラー',
},
'1501': {
en: 'Expiration month - Invalid format',
ja: '有効期限(月) - 書式チェックエラー',
},
'1502': {
en: 'Expiration date - Invalid format - The value should be future date and within the next 20 years.',
ja: '有効期限(年月)が不正です。(過去年月である、未来20年以降である)',
},
'1600': {
en: 'CVC - Invalid format',
ja: 'セキュリティコード - 書式チェックエラー',
},
'1601': {
en: 'CVC - Required if you use security code token',
ja: 'セキュリティコード - 必須エラー(セキュリティコードトークンの場合)',
},
'1700': {
en: 'Name - Invalid format',
ja: 'カード名義 - 書式チェックエラー',
},
'7000': {
en: 'Unsupported browser',
ja: '非対応のブラウザです。',
},
'7001': {
en: 'Connection failure',
ja: 'ペイジェントとの通信に失敗しました。',
},
'8000': {
en: 'Under maintenance',
ja: 'システムメンテナンス中です。',
},
'9000': {
en: 'Internal server error',
ja: 'ペイジェント決済システム内部エラー',
},
};

サポート

お探しのページは見つかりましたか?解決しない場合は、問い合わせフォームからお問い合わせいただくか、Slackコミュニティにご参加ください。