Skip to main content

Konbini (Convenience Store Payments)

Accept cash payments at 55,000+ convenience stores across Japan including 7-Eleven, FamilyMart, Lawson, and other major chains. Perfect for customers who prefer or require cash payment options.

Overview

Konbini (コンビニ決済) is a widely-used payment method in Japan that allows customers to pay for online purchases with cash at convenience stores. With over 55,000 convenience stores nationwide that are open 24/7, konbini payments provide accessibility and trust for customers who don't have credit cards or prefer not to use them online.

Key Features:

  • 55,000+ locations - Nationwide coverage across Japan
  • 24/7 availability - Convenience stores always open
  • No bank account needed - Cash payments only
  • Trusted method - Popular among all age groups
  • Receipt provided - Physical proof of payment
  • No chargebacks - Cash payments are final

Supported Stores

Major convenience store chains accepting konbini payments:

ChainLocationsNotes
7-Eleven21,000+Multi-Copy Machine system
FamilyMart16,000+Famiポート terminal
Lawson14,000+Loppi terminal
Mini Stop2,000+Loppi terminal
Seicomart1,100+Regional (Hokkaido)
Daily Yamazaki1,000+Receipt system

Supported Regions

RegionCurrencyMin AmountMax AmountPayment Window
JapanJPY¥100¥300,0007-14 days*

*Payment window can be customized per transaction (typically 3, 7, or 14 days)

How It Works

Customer Experience:

  1. Customer selects "Konbini" at checkout
  2. Chooses preferred convenience store chain
  3. Receives payment code and instructions
  4. Visits chosen convenience store within payment window
  5. Uses in-store terminal (Famiポート, Loppi, etc.) or shows code at counter
  6. Pays in cash and receives receipt
  7. Merchant ships order after payment confirmation

Typical completion time: 30 minutes - 7 days (depends on when customer visits store)

Implementation

Step 1: Create Konbini Source

curl https://api.omise.co/sources \
-u skey_test_YOUR_SECRET_KEY: \
-d "type=econtext" \
-d "amount=100000" \
-d "currency=JPY" \
-d "name=田中太郎" \
-d "email=tanaka@example.com" \
-d "phone_number=08012345678"

Response:

{
"object": "source",
"id": "src_test_5rt6s9vah5lkvi1rh9c",
"type": "econtext",
"flow": "offline",
"amount": 100000,
"currency": "JPY",
"name": "田中太郎",
"email": "tanaka@example.com",
"phone_number": "08012345678",
"references": {
"payment_code": "1234567890123",
"expires_at": "2024-02-15T23:59:59Z"
}
}

Step 2: Create Charge

curl https://api.omise.co/charges \
-u skey_test_YOUR_SECRET_KEY: \
-d "amount=100000" \
-d "currency=JPY" \
-d "source=src_test_5rt6s9vah5lkvi1rh9c" \
-d "return_uri=https://yourdomain.com/orders/confirmation"

Step 3: Display Payment Instructions

app.post('/checkout/konbini', async (req, res) => {
try {
const { amount, order_id, customer_name, customer_email, customer_phone } = req.body;

// Validate amount (¥100 - ¥300,000)
if (amount < 100 || amount > 300000) {
return res.status(400).json({
error: 'Amount must be between ¥100 and ¥300,000'
});
}

// Validate required fields
if (!customer_name || !customer_email || !customer_phone) {
return res.status(400).json({
error: 'Name, email, and phone number are required for Konbini payments'
});
}

// Create source
const source = await omise.sources.create({
type: 'econtext',
amount: amount,
currency: 'JPY',
name: customer_name,
email: customer_email,
phone_number: customer_phone
});

// Create charge
const charge = await omise.charges.create({
amount: amount,
currency: 'JPY',
source: source.id,
return_uri: `${process.env.BASE_URL}/orders/${order_id}/confirmation`,
metadata: {
order_id: order_id
}
});

// Display payment instructions
res.render('konbini-instructions', {
payment_code: charge.source.references.payment_code,
expires_at: charge.source.references.expires_at,
amount: charge.amount,
order_id: order_id,
customer_name: customer_name
});

} catch (error) {
console.error('Konbini error:', error);
res.status(500).json({ error: error.message });
}
});

Step 4: Payment Instructions Page

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>コンビニ決済 - お支払い方法</title>
<style>
.konbini-instructions {
max-width: 800px;
margin: 30px auto;
padding: 30px;
font-family: 'Noto Sans JP', sans-serif;
}
.payment-code {
font-size: 32px;
font-weight: bold;
text-align: center;
padding: 20px;
background: #f5f5f5;
border: 3px solid #333;
margin: 20px 0;
letter-spacing: 2px;
}
.expires-warning {
color: #d32f2f;
font-weight: bold;
text-align: center;
margin: 15px 0;
}
.store-instructions {
margin: 30px 0;
}
.store-card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 20px;
margin: 15px 0;
}
.store-logo {
height: 40px;
margin-right: 15px;
}
</style>
</head>
<body>
<div class="konbini-instructions">
<h1>ご注文ありがとうございます</h1>
<p>コンビニエンスストアでお支払いください</p>

<div class="order-summary">
<p><strong>注文番号:</strong> {{order_id}}</p>
<p><strong>お支払い金額:</strong> ¥{{amount | formatCurrency}}</p>
<p><strong>お名前:</strong> {{customer_name}}</p>
</div>

<h2>お支払い番号</h2>
<div class="payment-code">{{payment_code}}</div>

<div class="expires-warning">
⚠️ お支払い期限: {{expires_at | formatDate}}まで
</div>

<div class="store-instructions">
<h2>コンビニでのお支払い方法</h2>

<div class="store-card">
<h3>
<img src="/images/seven-eleven-logo.png" class="store-logo" alt="7-Eleven">
セブン-イレブン
</h3>
<ol>
<li>店内のマルチコピー機を操作</li>
<li>「お金をおろす・振込など」を選択</li>
<li>「インターネット支払い」を選択</li>
<li>お支払い番号を入力</li>
<li>申込券が印刷されたらレジで現金支払い</li>
</ol>
</div>

<div class="store-card">
<h3>
<img src="/images/familymart-logo.png" class="store-logo" alt="FamilyMart">
ファミリーマート
</h3>
<ol>
<li>店内のFamiポート端末を操作</li>
<li>「代金支払い」を選択</li>
<li>「イーコンテクスト」を選択</li>
<li>お支払い番号を入力</li>
<li>申込券が印刷されたらレジで現金支払い</li>
</ol>
</div>

<div class="store-card">
<h3>
<img src="/images/lawson-logo.png" class="store-logo" alt="Lawson">
ローソン / ミニストップ
</h3>
<ol>
<li>店内のLoppi端末を操作</li>
<li>「各種番号をお持ちの方」を選択</li>
<li>お支払い番号を入力</li>
<li>申込券が印刷されたらレジで現金支払い</li>
</ol>
</div>
</div>

<div class="important-notes">
<h3>重要事項</h3>
<ul>
<li>お支払い期限までにお支払いがない場合、ご注文はキャンセルされます</li>
<li>お支払い後のキャンセル・返金はできません</li>
<li>領収書が必要な場合は、レジでお申し出ください</li>
<li>お支払い確認後、商品を発送いたします(通常1-2営業日)</li>
</ul>
</div>

<div class="contact">
<p>ご不明な点がございましたら、カスタマーサポートまでお問い合わせください。</p>
<p>Email: support@example.com</p>
<p>お問い合わせ番号: {{order_id}}</p>
</div>

<button onclick="window.print()" class="print-button">この画面を印刷</button>
<button onclick="sendEmail()" class="email-button">メールで送信</button>
</div>

<script>
function sendEmail() {
// Send payment instructions via email
fetch('/api/send-konbini-instructions', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
order_id: '{{order_id}}',
email: '{{customer_email}}'
})
})
.then(() => alert('お支払い情報をメールで送信しました'))
.catch(() => alert('メール送信に失敗しました'));
}
</script>
</body>
</html>

Step 5: Handle Webhook

app.post('/webhooks/omise', (req, res) => {
const event = req.body;

if (event.key === 'charge.complete' && event.data.source.type === 'econtext') {
const charge = event.data;

if (charge.status === 'successful') {
// Payment received at convenience store
const orderId = charge.metadata.order_id;

processOrder(orderId);
sendPaymentConfirmation(orderId);
shipOrder(orderId);

console.log(`Konbini payment received: ${charge.id}`);
} else if (charge.status === 'failed' || charge.status === 'expired') {
// Payment expired without payment
const orderId = charge.metadata.order_id;

cancelOrder(orderId);
sendExpiryNotification(orderId);

console.log(`Konbini payment expired: ${charge.id}`);
}
}

res.sendStatus(200);
});

Complete Implementation Example

// Express.js server
const express = require('express');
const omise = require('omise')({
secretKey: process.env.OMISE_SECRET_KEY
});

const app = express();
app.use(express.json());

app.post('/checkout/konbini', async (req, res) => {
try {
const {
amount,
order_id,
customer_name,
customer_email,
customer_phone,
payment_deadline_days = 7
} = req.body;

// Validate amount (¥100 - ¥300,000)
if (amount < 100 || amount > 300000) {
return res.status(400).json({
error: 'Amount must be between ¥100 and ¥300,000'
});
}

// Validate required customer information
if (!customer_name || !customer_email || !customer_phone) {
return res.status(400).json({
error: 'Name, email, and phone are required for Konbini'
});
}

// Validate phone number format (Japanese)
if (!/^0\d{9,10}$/.test(customer_phone.replace(/-/g, ''))) {
return res.status(400).json({
error: 'Invalid Japanese phone number format'
});
}

// Create source
const source = await omise.sources.create({
type: 'econtext',
amount: amount,
currency: 'JPY',
name: customer_name,
email: customer_email,
phone_number: customer_phone
});

// Create charge
const charge = await omise.charges.create({
amount: amount,
currency: 'JPY',
source: source.id,
return_uri: `${process.env.BASE_URL}/orders/${order_id}/confirmation`,
metadata: {
order_id: order_id,
payment_deadline_days: payment_deadline_days
}
});

// Store payment information in database
await db.payments.create({
order_id: order_id,
charge_id: charge.id,
payment_code: charge.source.references.payment_code,
expires_at: charge.source.references.expires_at,
status: 'pending'
});

// Send payment instructions via email
await sendKonbiniInstructions({
email: customer_email,
payment_code: charge.source.references.payment_code,
amount: amount,
expires_at: charge.source.references.expires_at,
order_id: order_id
});

// Return payment instructions
res.json({
success: true,
payment_code: charge.source.references.payment_code,
expires_at: charge.source.references.expires_at,
instructions_url: `/orders/${order_id}/konbini-instructions`
});

} catch (error) {
console.error('Konbini error:', error);
res.status(500).json({ error: error.message });
}
});

// Email payment instructions
async function sendKonbiniInstructions(details) {
// Implementation depends on email service
const emailContent = `
ご注文ありがとうございます。

お支払い番号: ${details.payment_code}
お支払い金額: ¥${details.amount.toLocaleString('ja-JP')}
お支払い期限: ${new Date(details.expires_at).toLocaleString('ja-JP')}

お近くのコンビニエンスストアでお支払いください。
詳しいお支払い方法: ${process.env.BASE_URL}/orders/${details.order_id}/konbini-instructions
`;

// Send email
await emailService.send({
to: details.email,
subject: 'コンビニ決済のご案内',
body: emailContent
});
}

// Webhook handler
app.post('/webhooks/omise', async (req, res) => {
const event = req.body;

if (event.key === 'charge.complete') {
const charge = event.data;

if (charge.source.type === 'econtext') {
const orderId = charge.metadata.order_id;

if (charge.status === 'successful') {
// Payment received
await db.payments.update(
{ charge_id: charge.id },
{ status: 'paid', paid_at: new Date() }
);
await db.orders.update(
{ id: orderId },
{ status: 'paid', payment_method: 'konbini' }
);

// Send confirmation email
const order = await db.orders.findOne({ id: orderId });
await sendPaymentConfirmation(order);

// Initiate shipping
await initiateShipping(orderId);

console.log(`Konbini payment successful: ${charge.id}, order: ${orderId}`);

} else if (charge.status === 'expired') {
// Payment window expired
await db.payments.update(
{ charge_id: charge.id },
{ status: 'expired' }
);
await db.orders.update(
{ id: orderId },
{ status: 'cancelled', cancellation_reason: 'payment_expired' }
);

// Send expiry notification
const order = await db.orders.findOne({ id: orderId });
await sendExpiryNotification(order);

console.log(`Konbini payment expired: ${charge.id}, order: ${orderId}`);
}
}
}

res.sendStatus(200);
});

// Helper functions
async function sendPaymentConfirmation(order) {
// Send order confirmation email
}

async function sendExpiryNotification(order) {
// Notify customer of expired payment
}

async function initiateShipping(orderId) {
// Begin order fulfillment process
}

app.listen(3000);

Refund Support

No Refunds

Konbini payments do NOT support refunds through Omise. Once a customer pays in cash at a convenience store, the payment cannot be refunded via API. You must handle refunds manually (e.g., bank transfer to customer).

Common Issues & Troubleshooting

Issue: Payment expired without payment

Cause: Customer didn't pay within the payment window

Solution:

// Send reminder emails before expiry
async function sendPaymentReminders(orderId) {
const payment = await db.payments.findOne({ order_id: orderId });
const expiresAt = new Date(payment.expires_at);
const now = new Date();
const hoursUntilExpiry = (expiresAt - now) / (1000 * 60 * 60);

// Send reminder 24 hours before expiry
if (hoursUntilExpiry <= 24 && hoursUntilExpiry > 23) {
await sendEmail({
to: payment.customer_email,
subject: 'お支払い期限が近づいています',
body: `お支払い期限まで残り24時間です。お早めにお支払いください。`
});
}
}

Issue: Customer can't find payment terminal

Cause: Customer unfamiliar with convenience store payment process

Solution:

<!-- Provide detailed visual instructions -->
<div class="visual-guide">
<h3>お支払い方法(写真付き)</h3>
<div class="step-photos">
<img src="/images/konbini/step1-terminal.jpg" alt="端末の場所">
<p>1. 店内の端末を探します</p>

<img src="/images/konbini/step2-menu.jpg" alt="メニュー画面">
<p>2. 「代金支払い」を選択</p>

<img src="/images/konbini/step3-code.jpg" alt="番号入力">
<p>3. お支払い番号を入力</p>

<img src="/images/konbini/step4-ticket.jpg" alt="申込券">
<p>4. 申込券を持ってレジへ</p>
</div>
</div>

Issue: Payment code not working

Cause: Customer entering code incorrectly or code expired

Solution:

function validatePaymentCode(paymentCode) {
const payment = await db.payments.findOne({ payment_code: paymentCode });

if (!payment) {
return {
error: 'お支払い番号が見つかりません',
suggestion: 'お支払い番号をもう一度ご確認ください'
};
}

const now = new Date();
const expiresAt = new Date(payment.expires_at);

if (now > expiresAt) {
return {
error: 'お支払い期限が過ぎています',
suggestion: '新しいご注文をお願いいたします'
};
}

return { valid: true };
}

Issue: Customer lost payment code

Cause: Customer didn't save payment instructions

Solution:

// Provide code lookup feature
app.get('/orders/:orderId/resend-payment-code', async (req, res) => {
const { orderId } = req.params;
const { email } = req.query;

const order = await db.orders.findOne({ id: orderId });
const payment = await db.payments.findOne({ order_id: orderId });

// Verify email matches
if (order.customer_email !== email) {
return res.status(403).json({ error: 'Email mismatch' });
}

// Resend instructions
await sendKonbiniInstructions({
email: email,
payment_code: payment.payment_code,
amount: order.amount,
expires_at: payment.expires_at,
order_id: orderId
});

res.json({ success: true, message: 'お支払い情報を再送信しました' });
});

Best Practices

1. Provide Clear Instructions in Japanese

// All customer-facing text should be in Japanese
const instructions = {
title: 'コンビニでのお支払い方法',
steps: [
'最寄りのコンビニエンスストアに行きます',
'店内の端末でお支払い番号を入力します',
'印刷された申込券をレジに持っていきます',
'現金でお支払いください',
'お支払い後、確認メールが届きます'
]
};

2. Send Multiple Notifications

// Email timeline
async function sendKonbiniNotifications(order, payment) {
// Immediate: Payment instructions
await sendPaymentInstructions(order, payment);

// Day 3: Reminder if unpaid
scheduleEmail(order, payment, 3, 'reminder');

// Day 6: Final reminder (for 7-day window)
scheduleEmail(order, payment, 6, 'final_reminder');

// Expiry: Cancellation notice
scheduleEmail(order, payment, 7, 'expiry');
}

3. Handle Different Payment Windows

function calculateExpiryDate(days) {
const options = [3, 7, 14]; // Common windows

if (!options.includes(days)) {
throw new Error('Payment window must be 3, 7, or 14 days');
}

const expiry = new Date();
expiry.setDate(expiry.getDate() + days);
expiry.setHours(23, 59, 59, 999); // End of day

return expiry;
}

4. Validate Japanese Customer Information

function validateJapaneseCustomer(data) {
// Name validation (supports kanji, hiragana, katakana)
if (!/^[\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FAF\u3000-\u303F\s]+$/.test(data.name)) {
return 'お名前は日本語で入力してください';
}

// Phone number validation (Japanese format)
const phone = data.phone.replace(/-/g, '');
if (!/^0\d{9,10}$/.test(phone)) {
return '電話番号の形式が正しくありません';
}

// Email validation
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) {
return 'メールアドレスの形式が正しくありません';
}

return null; // Valid
}

5. Optimize Amount Limits

function validateKonbiniAmount(amount) {
const MIN = 100; // ¥100
const MAX = 300000; // ¥300,000

if (amount < MIN) {
return `最小金額は¥${MIN.toLocaleString('ja-JP')}です`;
}

if (amount > MAX) {
return `最大金額は¥${MAX.toLocaleString('ja-JP')}です`;
}

return null; // Valid
}

6. Provide Printable Instructions

// Make instructions printer-friendly
function generatePrintableInstructions(payment) {
return `
<div class="printable-instructions" style="font-size: 14pt; padding: 20mm;">
<h1>コンビニお支払い番号</h1>
<div class="payment-code" style="font-size: 24pt; font-weight: bold; padding: 20px; border: 3px solid black;">
${payment.payment_code}
</div>
<p>お支払い期限: ${formatDate(payment.expires_at)}</p>
<p>お支払い金額: ¥${payment.amount.toLocaleString('ja-JP')}</p>
<!-- Store-specific instructions -->
</div>
`;
}

FAQ

What is Konbini payment?

Konbini (convenience store) payment allows customers to pay for online purchases with cash at any of 55,000+ convenience stores across Japan including 7-Eleven, FamilyMart, and Lawson. It's ideal for customers who prefer or require cash payment options.

Do customers need a bank account or credit card?

No, Konbini payment is cash-only and doesn't require any bank account or credit card. Customers simply pay in cash at the convenience store counter.

What are the transaction limits?
  • Minimum: ¥100 per transaction
  • Maximum: ¥300,000 per transaction
  • Payment window: Typically 3-14 days (customizable)
Which convenience stores accept Konbini payments?

Major chains include:

  • 7-Eleven (21,000+ locations)
  • FamilyMart (16,000+)
  • Lawson (14,000+)
  • Mini Stop (2,000+)
  • Seicomart (1,100+ in Hokkaido)
  • Daily Yamazaki (1,000+)
How long does it take for payment to be confirmed?

Payment confirmation is typically instant or within a few minutes after the customer pays at the convenience store. Webhooks notify you immediately when payment is received.

Can I refund Konbini payments?

No, Konbini payments cannot be refunded through the Omise API since they are cash payments. If you need to refund a customer, you must arrange a manual refund (e.g., bank transfer) outside of Omise.

What happens if customer doesn't pay before expiry?

If the payment window expires without payment, the charge status changes to "expired" and you'll receive a webhook notification. You should cancel the order and notify the customer. The customer cannot pay after expiry - they would need to place a new order.

Do I need to provide Japanese language support?

Yes, absolutely essential. Konbini payment instructions, payment codes, and all customer communications should be in Japanese. Most Konbini users are Japanese residents who expect Japanese language support.

When should I ship the order?

Only ship orders after receiving payment confirmation via webhook. Unlike credit cards, Konbini payments are not instant - customers may pay hours or days after placing the order. Wait for the charge.complete webhook with status: successful before shipping.

Testing

Test Mode

Konbini can be tested using your test API keys. In test mode:

Test Credentials:

  • Use test API keys (skey_test_xxx)
  • Currency: JPY (Japanese Yen)
  • Use valid Japanese name, email, and phone number formats
  • No actual convenience store visit required

Test Flow:

  1. Create source with test customer details (name, email, phone)
  2. Create charge with test API keys
  3. Payment code is generated in test mode
  4. Use Omise Dashboard Actions to mark charge as successful/failed/expired
  5. Verify webhook handling for all statuses

Testing Implementation:

// Test Konbini payment
const source = await omise.sources.create({
type: 'econtext',
amount: 100000, // ¥100,000
currency: 'JPY',
name: '田中太郎', // Test Japanese name
email: 'test@example.com',
phone_number: '08012345678' // Test Japanese phone
});

const charge = await omise.charges.create({
amount: 100000,
currency: 'JPY',
source: source.id,
return_uri: 'https://example.com/orders/confirmation',
metadata: {
order_id: 'TEST-001'
}
});

console.log('Payment code:', charge.source.references.payment_code);
console.log('Expires at:', charge.source.references.expires_at);

Test Scenarios:

  • Successful payment: Simulate customer paying at store
  • Expired payment: Test payment window expiration (no payment made)
  • Failed payment: Test payment failure scenarios
  • Payment reminders: Test reminder email system
  • Japanese text: Verify all Japanese language content displays correctly
  • Amount limits: Test ¥100 minimum and ¥300,000 maximum
  • Phone validation: Test Japanese phone number format validation
  • Payment instructions: Verify instructions display for each store chain
  • Webhook delivery: Verify webhooks for successful, failed, and expired statuses

Important Notes:

  • Test mode payment codes are for testing only
  • Customers cannot actually pay test codes at real convenience stores
  • Use dashboard to simulate payment status changes
  • Test Japanese character encoding (name, instructions)
  • Verify payment window expiration handling (typically 7 days)
  • Test email notifications (instructions, reminders, confirmation)
  • Test all convenience store chains' instructions display correctly

For comprehensive testing guidelines, see the Testing Documentation.

Next Steps

  1. Create Konbini source
  2. Display payment instructions in Japanese
  3. Set up webhook handling
  4. Implement payment reminders
  5. Test payment flow
  6. Go live