Square
決済・POSプラットフォーム
Square
概要
Squareは、対面・オンライン決済、POS システム、ビジネス管理ツールを統合した包括的な決済プラットフォームです。小売店、レストラン、サービス業を中心に、シンプルなハードウェア・ソフトウェア統合により、中小企業の決済・業務管理を革新しています。2025年には、ビットコイン決済統合、AI搭載の新ハードウェア、Square Releasesプログラムなど、次世代の決済・ビジネスソリューションを展開しています。
詳細
Squareは2009年に設立され、カードリーダーの革新から始まり、現在では決済処理、在庫管理、顧客管理、給与計算、融資まで提供する統合ビジネスプラットフォームに成長しました。2025年現在、新しいSquare Handheldデバイス、Bitcoin統合による暗号通貨決済、AI機能による売上予測・在庫最適化など、最新技術を活用したビジネスソリューションを展開しています。
主な特徴
- 統合POS: ハードウェア・ソフトウェア一体型のPOSシステム
- 多様な決済方法: カード、デジタルウォレット、現金、ビットコイン対応
- ビジネス管理: 在庫、顧客、従業員、会計の統合管理
- ハードウェア生態系: Square Terminal、Reader、Stand、Handheld等
- 業界特化: レストラン、小売、美容、サービス業向けソリューション
- 金融サービス: Square Capital(事業資金調達)、銀行サービス
- 開発者API: カスタム統合とアプリ開発のための包括的API
- AI・分析: 売上予測、在庫最適化、顧客インサイト
対応決済方法
- カード決済: Visa、Mastercard、American Express、Discover、JCB
- デジタルウォレット: Apple Pay、Google Pay、Samsung Pay
- 現金決済: POSでの現金管理・レポート
- ビットコイン: Square Cash Appとの統合(2025年新機能)
- 後払い決済: Afterpay、Klarna統合
- ギフトカード: Square独自のギフトカードシステム
メリット・デメリット
メリット
- 簡単セットアップ: プラグアンドプレイのハードウェア
- 統合ソリューション: 決済から業務管理まで一元化
- 透明な料金: 明確で予測可能な手数料体系
- ハードウェア品質: 堅牢で使いやすいPOSデバイス
- 業界特化: 各業界のニーズに特化した機能
- 迅速入金: 最短翌営業日の資金調達
- 無料プラン: 基本機能は無料で利用可能
デメリット
- 手数料コスト: 取引ごとの手数料(競合より高い場合)
- カスタマイズ制限: プラットフォーム設計による制約
- 高度機能: 一部機能は有料プランが必要
- 地域制限: 日本など一部地域での機能制限
- インターネット依存: オフライン機能の制限
- 競合比較: 特定用途では他プラットフォームが優位
参考ページ
- Square公式サイト
- Square Developer Platform
- Square API Documentation
- Square App Marketplace
- Square Support Center
実装例
1. 基本セットアップ
# Square SDK インストール
npm install squareup
# Square Developer Dashboard設定
# 1. https://developer.squareup.com/ でアプリケーション作成
# 2. Application ID と Access Token を取得
# 3. Webhook Signature Key を設定
# 開発環境用 Sandbox 設定
export SQUARE_APPLICATION_ID="sandbox-sq0idp-xxxxx"
export SQUARE_ACCESS_TOKEN="sandbox-sq0atb-xxxxx"
export SQUARE_WEBHOOK_SIGNATURE_KEY="xxxxx"
export SQUARE_ENVIRONMENT="sandbox" # or "production"
# Square CLI インストール(開発・テスト用)
curl -L https://github.com/square/square-cli/releases/download/v1.0.0/square-cli-linux -o square-cli
chmod +x square-cli
2. 決済処理基本実装
// Square SDK 初期化
const { Client, Environment } = require('squareup');
const client = new Client({
accessToken: process.env.SQUARE_ACCESS_TOKEN,
environment: process.env.SQUARE_ENVIRONMENT === 'production'
? Environment.Production
: Environment.Sandbox
});
const paymentsApi = client.paymentsApi;
const ordersApi = client.ordersApi;
const customersApi = client.customersApi;
// 基本決済処理
async function processPayment(paymentRequest) {
try {
// 注文作成
const order = {
order: {
locationId: process.env.SQUARE_LOCATION_ID,
lineItems: paymentRequest.items.map(item => ({
name: item.name,
quantity: item.quantity.toString(),
basePriceMoney: {
amount: item.price, // 円の場合は最小単位(円)
currency: 'JPY'
}
})),
taxes: [
{
name: '消費税',
percentage: '10.0',
scope: 'ORDER'
}
]
}
};
const { result: orderResult } = await ordersApi.createOrder(order);
const createdOrder = orderResult.order;
// 決済処理
const payment = {
sourceId: paymentRequest.sourceId, // カードnonce
amountMoney: {
amount: parseInt(createdOrder.totalMoney.amount),
currency: 'JPY'
},
orderId: createdOrder.id,
autocomplete: true,
locationId: process.env.SQUARE_LOCATION_ID,
referenceId: paymentRequest.referenceId || `order-${Date.now()}`,
note: paymentRequest.note || '決済処理',
// 顧客情報
buyerEmailAddress: paymentRequest.customerEmail,
// 請求先住所
billingAddress: {
addressLine1: paymentRequest.billingAddress.line1,
addressLine2: paymentRequest.billingAddress.line2,
locality: paymentRequest.billingAddress.city,
postalCode: paymentRequest.billingAddress.postalCode,
country: 'JP'
}
};
const { result: paymentResult } = await paymentsApi.createPayment(payment);
return {
success: true,
paymentId: paymentResult.payment.id,
orderId: createdOrder.id,
receiptNumber: paymentResult.payment.receiptNumber,
cardDetails: paymentResult.payment.cardDetails,
totalAmount: createdOrder.totalMoney
};
} catch (error) {
console.error('決済処理エラー:', error);
return {
success: false,
error: error.message,
errorCode: error.code
};
}
}
// 顧客作成・管理
async function createCustomer(customerData) {
try {
const customer = {
givenName: customerData.firstName,
familyName: customerData.lastName,
emailAddress: customerData.email,
phoneNumber: customerData.phone,
address: {
addressLine1: customerData.address.line1,
addressLine2: customerData.address.line2,
locality: customerData.address.city,
postalCode: customerData.address.postalCode,
country: 'JP'
},
note: customerData.note || '',
referenceId: customerData.referenceId
};
const { result } = await customersApi.createCustomer(customer);
return result.customer;
} catch (error) {
console.error('顧客作成エラー:', error);
throw error;
}
}
// 返金処理
async function refundPayment(paymentId, refundAmount, reason) {
try {
const refund = {
amountMoney: {
amount: refundAmount,
currency: 'JPY'
},
paymentId: paymentId,
reason: reason || '顧客要請による返金'
};
const { result } = await paymentsApi.refundPayment(refund);
return {
success: true,
refundId: result.refund.id,
status: result.refund.status,
refundAmount: result.refund.amountMoney
};
} catch (error) {
console.error('返金処理エラー:', error);
throw error;
}
}
3. 在庫・商品カタログ管理
// 商品カタログ管理
const catalogApi = client.catalogApi;
const inventoryApi = client.inventoryApi;
// 商品作成
async function createProduct(productData) {
try {
const catalogObject = {
type: 'ITEM',
id: `#${productData.sku}`,
itemData: {
name: productData.name,
description: productData.description,
categoryId: productData.categoryId,
productType: 'REGULAR',
skipModifierScreen: false,
variations: productData.variations.map((variation, index) => ({
type: 'ITEM_VARIATION',
id: `#${productData.sku}_variation_${index}`,
itemVariationData: {
itemId: `#${productData.sku}`,
name: variation.name,
sku: variation.sku,
pricingType: 'FIXED_PRICING',
priceMoney: {
amount: variation.price,
currency: 'JPY'
},
trackInventory: true,
stockable: true
}
}))
}
};
const upsertRequest = {
idempotencyKey: `create-product-${Date.now()}`,
object: catalogObject
};
const { result } = await catalogApi.upsertCatalogObject(upsertRequest);
return result.catalogObject;
} catch (error) {
console.error('商品作成エラー:', error);
throw error;
}
}
// 在庫管理
async function updateInventory(catalogObjectId, quantity, locationId) {
try {
const change = {
type: 'ADJUSTMENT',
adjustment: {
catalogObjectId: catalogObjectId,
fromState: 'IN_STOCK',
toState: 'IN_STOCK',
locationId: locationId,
quantity: quantity.toString()
}
};
const inventoryChange = {
idempotencyKey: `inventory-update-${Date.now()}`,
changes: [change]
};
const { result } = await inventoryApi.batchChangeInventory(inventoryChange);
return result.changes;
} catch (error) {
console.error('在庫更新エラー:', error);
throw error;
}
}
// 在庫レベル取得
async function getInventoryLevels(catalogObjectIds, locationIds) {
try {
const { result } = await inventoryApi.batchRetrieveInventoryCounts({
catalogObjectIds: catalogObjectIds,
locationIds: locationIds,
states: ['IN_STOCK']
});
return result.counts.map(count => ({
catalogObjectId: count.catalogObjectId,
locationId: count.locationId,
quantity: parseInt(count.quantity),
state: count.state
}));
} catch (error) {
console.error('在庫取得エラー:', error);
throw error;
}
}
4. 注文・配送管理
// 注文管理システム
class OrderManager {
constructor(squareClient) {
this.ordersApi = squareClient.ordersApi;
this.paymentsApi = squareClient.paymentsApi;
}
// 注文検索・一覧
async searchOrders(filters = {}) {
try {
const query = {
filter: {},
sort: {
sortField: 'CREATED_AT',
sortOrder: 'DESC'
}
};
// フィルター設定
if (filters.locationIds) {
query.filter.locationFilter = {
locationIds: filters.locationIds
};
}
if (filters.dateRange) {
query.filter.dateTimeFilter = {
createdAt: {
startAt: filters.dateRange.startAt,
endAt: filters.dateRange.endAt
}
};
}
if (filters.states) {
query.filter.stateFilter = {
states: filters.states
};
}
const searchRequest = {
query: query,
limit: filters.limit || 50,
returnEntries: true
};
const { result } = await this.ordersApi.searchOrders(searchRequest);
return result.orders || [];
} catch (error) {
console.error('注文検索エラー:', error);
throw error;
}
}
// 注文詳細取得
async getOrderDetails(orderId) {
try {
const { result } = await this.ordersApi.retrieveOrder(orderId);
return result.order;
} catch (error) {
console.error('注文詳細取得エラー:', error);
throw error;
}
}
// 注文ステータス更新
async updateOrderStatus(orderId, newState, version) {
try {
const updateRequest = {
order: {
state: newState,
version: version
}
};
const { result } = await this.ordersApi.updateOrder(orderId, updateRequest);
return result.order;
} catch (error) {
console.error('注文ステータス更新エラー:', error);
throw error;
}
}
// 配送料計算
calculateShippingCost(order, shippingMethod) {
const baseShipping = {
'標準配送': 500,
'速達': 1000,
'翌日配達': 1500
};
let shippingCost = baseShipping[shippingMethod] || 500;
// 注文金額による配送料無料設定
const orderTotal = parseInt(order.totalMoney.amount);
if (orderTotal >= 10000) { // 10,000円以上で送料無料
shippingCost = 0;
}
return shippingCost;
}
// 注文フルフィルメント
async fulfillOrder(orderId, fulfillmentData) {
try {
const fulfillment = {
type: fulfillmentData.type || 'SHIPMENT',
state: 'PREPARED',
shipmentDetails: {
recipient: fulfillmentData.recipient,
carrier: fulfillmentData.carrier,
shippingNote: fulfillmentData.shippingNote,
trackingNumber: fulfillmentData.trackingNumber,
expectedShippedAt: fulfillmentData.expectedShippedAt
}
};
const updateRequest = {
order: {
fulfillments: [fulfillment],
version: fulfillmentData.orderVersion
}
};
const { result } = await this.ordersApi.updateOrder(orderId, updateRequest);
return result.order;
} catch (error) {
console.error('注文フルフィルメント エラー:', error);
throw error;
}
}
}
5. ハードウェア統合・POS実装
// Square POS API統合(端末向け)
class SquarePOSIntegration {
constructor(applicationId, locationId) {
this.applicationId = applicationId;
this.locationId = locationId;
}
// Square POSアプリとの連携
generatePOSURL(transactionData) {
const baseUrl = 'square-commerce-v1://payment/create';
const params = new URLSearchParams({
'data': JSON.stringify({
client_id: this.applicationId,
version: '1.3',
notes: transactionData.notes || '',
amount_money: {
amount: transactionData.amount,
currency_code: 'JPY'
},
callback_url: transactionData.callbackUrl,
version: '1.3',
options: {
supported_tender_types: [
'CREDIT_CARD',
'CASH',
'OTHER'
]
}
})
});
return `${baseUrl}?${params.toString()}`;
}
// ハードウェアイベント処理
async handleHardwareEvent(eventType, eventData) {
switch (eventType) {
case 'CARD_INSERTED':
return await this.processCardPayment(eventData);
case 'CASH_DRAWER_OPENED':
return await this.recordCashDrawerEvent(eventData);
case 'RECEIPT_PRINTED':
return await this.confirmReceiptPrint(eventData);
default:
console.log(`未対応ハードウェアイベント: ${eventType}`);
}
}
// Square Terminal API(2025年新機能)
async sendTerminalCommand(deviceId, command) {
try {
const terminalApi = client.terminalApi;
const terminalCheckout = {
amountMoney: {
amount: command.amount,
currency: 'JPY'
},
deviceOptions: {
deviceId: deviceId,
skipReceiptScreen: command.skipReceipt || false,
collectSignature: command.collectSignature || true
},
note: command.note || 'Terminal決済',
orderId: command.orderId
};
const { result } = await terminalApi.createTerminalCheckout(terminalCheckout);
return result.checkout;
} catch (error) {
console.error('Terminal コマンド送信エラー:', error);
throw error;
}
}
}
// レストラン向けPOS機能
class RestaurantPOS {
constructor(squareClient, locationId) {
this.ordersApi = squareClient.ordersApi;
this.paymentsApi = squareClient.paymentsApi;
this.locationId = locationId;
}
// テーブル注文管理
async createTableOrder(tableNumber, items, customerId = null) {
try {
const order = {
locationId: this.locationId,
referenceId: `table-${tableNumber}-${Date.now()}`,
source: {
name: `テーブル ${tableNumber}`
},
lineItems: items.map(item => ({
name: item.name,
quantity: item.quantity.toString(),
basePriceMoney: {
amount: item.price,
currency: 'JPY'
},
modifiers: item.modifiers || [],
note: item.specialRequests || ''
})),
serviceCharges: [
{
name: 'サービス料',
percentage: '10.0',
calculationPhase: 'SUBTOTAL_PHASE'
}
],
metadata: {
tableNumber: tableNumber.toString(),
orderType: 'DINE_IN',
customerId: customerId
}
};
const { result } = await this.ordersApi.createOrder({ order });
return result.order;
} catch (error) {
console.error('テーブル注文作成エラー:', error);
throw error;
}
}
// キッチン表示用注文データ
formatKitchenDisplay(order) {
return {
orderId: order.id,
tableNumber: order.metadata?.tableNumber,
orderTime: order.createdAt,
items: order.lineItems.map(item => ({
name: item.name,
quantity: item.quantity,
modifiers: item.modifiers?.map(mod => mod.name),
specialRequests: item.note,
status: 'PENDING' // PENDING, PREPARING, READY
})),
totalItems: order.lineItems.length,
estimatedTime: this.calculateCookingTime(order.lineItems)
};
}
// 調理時間計算
calculateCookingTime(lineItems) {
const baseTimes = {
'サラダ': 5,
'スープ': 10,
'パスタ': 15,
'ピザ': 20,
'ステーキ': 25
};
let maxTime = 0;
lineItems.forEach(item => {
const itemTime = baseTimes[item.name] || 10;
maxTime = Math.max(maxTime, itemTime);
});
return maxTime;
}
}
6. 2025年新機能:ビットコイン・AI統合
// Bitcoin決済統合(2025年新機能)
class BitcoinPaymentService {
constructor(squareClient) {
this.paymentsApi = squareClient.paymentsApi;
this.customersApi = squareClient.customersApi;
}
// Bitcoin決済処理
async processBitcoinPayment(paymentRequest) {
try {
// Square Cash App統合によるBitcoin決済
const payment = {
sourceId: paymentRequest.bitcoinSourceId,
amountMoney: {
amount: paymentRequest.amount,
currency: 'JPY'
},
locationId: process.env.SQUARE_LOCATION_ID,
note: 'Bitcoin決済',
// Bitcoin特有の設定
cashDetails: {
buyerSuppliedMoney: {
amount: paymentRequest.bitcoinAmount,
currency: 'BTC'
}
},
metadata: {
paymentType: 'BITCOIN',
exchangeRate: paymentRequest.btcToJpyRate,
bitcoinAddress: paymentRequest.bitcoinAddress
}
};
const { result } = await this.paymentsApi.createPayment(payment);
return {
success: true,
paymentId: result.payment.id,
bitcoinAmount: paymentRequest.bitcoinAmount,
jpyAmount: paymentRequest.amount,
exchangeRate: paymentRequest.btcToJpyRate,
transactionHash: result.payment.metadata?.transactionHash
};
} catch (error) {
console.error('Bitcoin決済エラー:', error);
throw error;
}
}
// Bitcoin為替レート取得
async getBitcoinExchangeRate() {
try {
// Square Cash App APIまたは外部為替API使用
const response = await fetch('https://api.cashapp.com/v1/bitcoin/rate');
const data = await response.json();
return {
btcToUsd: data.rate,
btcToJpy: data.rate * 150, // USD/JPYレート(実際のAPIから取得)
timestamp: data.timestamp
};
} catch (error) {
console.error('Bitcoin為替レート取得エラー:', error);
throw error;
}
}
}
// AI分析・予測(2025年新機能)
class AIAnalyticsService {
constructor(squareClient) {
this.ordersApi = squareClient.ordersApi;
this.catalogApi = squareClient.catalogApi;
this.customersApi = squareClient.customersApi;
}
// 売上予測
async predictSalesRevenue(locationId, days = 30) {
try {
// 過去の売上データ取得
const historicalData = await this.getHistoricalSales(locationId, 90);
// 機械学習モデルによる予測(Square AIサービス使用)
const prediction = await this.callSquareAI('sales_prediction', {
historicalData: historicalData,
predictionDays: days,
seasonalFactors: await this.getSeasonalFactors(),
externalFactors: await this.getExternalFactors()
});
return {
predictedRevenue: prediction.revenue,
confidence: prediction.confidence,
factors: prediction.influencingFactors,
dailyBreakdown: prediction.dailyPredictions
};
} catch (error) {
console.error('売上予測エラー:', error);
throw error;
}
}
// 在庫最適化AI
async optimizeInventory(locationId) {
try {
const [currentInventory, salesVelocity, seasonalTrends] = await Promise.all([
this.getCurrentInventoryLevels(locationId),
this.getSalesVelocity(locationId),
this.getSeasonalTrends(locationId)
]);
const optimization = await this.callSquareAI('inventory_optimization', {
currentInventory,
salesVelocity,
seasonalTrends,
leadTimes: await this.getSupplierLeadTimes(),
storageCapacity: await this.getStorageCapacity(locationId)
});
return {
recommendations: optimization.restockSuggestions,
overstockAlerts: optimization.overstockItems,
understockAlerts: optimization.understockItems,
optimalOrderQuantities: optimization.orderQuantities,
costSavings: optimization.projectedSavings
};
} catch (error) {
console.error('在庫最適化エラー:', error);
throw error;
}
}
// Square AI API呼び出し(2025年新機能)
async callSquareAI(model, data) {
try {
const response = await fetch('https://ai.squareup.com/v1/predict', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SQUARE_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
'Square-Version': '2025-01-01'
},
body: JSON.stringify({
model: model,
input: data,
location_id: process.env.SQUARE_LOCATION_ID
})
});
if (!response.ok) {
throw new Error(`Square AI API エラー: ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error('Square AI API エラー:', error);
throw error;
}
}
// 顧客行動分析
async analyzeCustomerBehavior(customerId) {
try {
const customerData = await this.getCustomerData(customerId);
const analysis = await this.callSquareAI('customer_analysis', {
transactionHistory: customerData.orders,
visitFrequency: customerData.visitPattern,
purchasePreferences: customerData.preferences,
seasonalBehavior: customerData.seasonality
});
return {
lifetimeValue: analysis.clv,
churnRisk: analysis.churnProbability,
nextPurchasePrediction: analysis.nextPurchase,
recommendedProducts: analysis.productRecommendations,
optimalContactTime: analysis.engagementTiming,
loyaltyScore: analysis.loyaltyIndex
};
} catch (error) {
console.error('顧客行動分析エラー:', error);
throw error;
}
}
}
// Square Releases統合(2025年新機能)
class SquareReleasesIntegration {
constructor(squareClient) {
this.client = squareClient;
}
// 新機能アクセス
async enableBetaFeature(featureName) {
try {
const response = await fetch('https://api.squareup.com/v1/releases/enable', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SQUARE_ACCESS_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
feature: featureName,
location_id: process.env.SQUARE_LOCATION_ID
})
});
return await response.json();
} catch (error) {
console.error('ベータ機能有効化エラー:', error);
throw error;
}
}
// 利用可能な新機能一覧
async getAvailableFeatures() {
try {
const response = await fetch('https://api.squareup.com/v1/releases/available', {
headers: {
'Authorization': `Bearer ${process.env.SQUARE_ACCESS_TOKEN}`
}
});
return await response.json();
} catch (error) {
console.error('利用可能機能取得エラー:', error);
throw error;
}
}
}