Square

決済プラットフォームPOS小売レストランビットコインAIハードウェア統合

決済・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デバイス
  • 業界特化: 各業界のニーズに特化した機能
  • 迅速入金: 最短翌営業日の資金調達
  • 無料プラン: 基本機能は無料で利用可能

デメリット

  • 手数料コスト: 取引ごとの手数料(競合より高い場合)
  • カスタマイズ制限: プラットフォーム設計による制約
  • 高度機能: 一部機能は有料プランが必要
  • 地域制限: 日本など一部地域での機能制限
  • インターネット依存: オフライン機能の制限
  • 競合比較: 特定用途では他プラットフォームが優位

参考ページ

実装例

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;
    }
  }
}