agriweather/laravel-ezpay-invoice

適用於 Laravel 的 ezPay 電子發票 API 套件

v1.0.0-beta.6 2025-09-26 10:00 UTC

README

Latest Version on Packagist Software License GitHub Tests Action Status Total Downloads

Laravel ezPay Invoice 是適用於 Laravel 的 ezPay 電子發票 API 套件。由 Lucas Yang 創建,並由 阿龜微氣候天眼通 團隊維護。

套件功能

  • 🧾 電子發票 API
  • 🌏 電子發票 API (境外電商版)
  • 🅰 字軌管理 API
  • 📱 手機條碼與捐證碼驗證 API

目錄

版本需求

版本 PHP 版本 Laravel 版本
1.x >=8.1 >=9.x

開始使用

使用 Composer 安裝套件:

composer require agriweather/laravel-ezpay-invoice

發布設置檔案:

php artisan vendor:publish --tag=ezpay-invoice-config

前往 ezPay 電子發票官方網站註冊帳號(測試時需在測試環境註冊測試帳號)並建立商店。

在 ezPay 電子發票平台開立發票需要購買額度,如果額度不足,請前往「發票管理」>「管理設定」>「使用狀況」進行購買(測試環境同樣需要點擊購買,但不會實際收費)。

進入 ezPay 電子發票的「商店管理」頁面,找到對應的商店,複製商店串接 API 的商店代號、HashKeyHashIV,然後將這些資訊設定到 .env 檔案中的 EZPAY_INVOICE_MERCHANT_ID 等參數:

EZPAY_INVOICE_ENV=test
EZPAY_INVOICE_MERCHANT_ID=your-merchant-id
EZPAY_INVOICE_MERCHANT_HASH_KEY=your-merchant-hash-key
EZPAY_INVOICE_MERCHANT_HASH_IV=your-merchant-hash-iv

EZPAY_INVOICE_ENV 可以設定為 test(測試環境)或 production(正式環境)。

完成設定後,即可開始測試電子發票的開立:

use Agriweather\EzPayInvoice\Enums\Invoice\TaxType;
use Agriweather\EzPayInvoice\Facades\EzPayInvoice;

$result = EzPayInvoice::invoice()
    ->create()
    ->withOrder('Order'.time())
    ->forConsumer('John Doe')
    ->withEmail('customer@example.com')
    ->withAddress('台北市信義區信義路五段7號')
    ->withItem('測試商品', quantity: 1, unit: '', price: 1000, amount: 1000)
    ->withTax(TaxType::TAXABLE, 5)
    ->withAmount(1000, 50, 1050)
    ->issue();

$result->invoiceNumber() // 發票號碼:'AB12345678'
$result->randomNumber() // 發票隨機碼:'1234'

電子發票 API

開立電子發票

開立 B2C 電子發票的基本範例:

use Agriweather\EzPayInvoice\Facades\EzPayInvoice;

$result = EzPayInvoice::invoice()
    ->create()
    ->withOrder('Order001') // 訂單編號
    ->forConsumer('John Doe') // 消費者姓名
    ->withEmail('customer@example.com') // 消費者電子信箱
    ->withAddress('台北市信義區信義路五段7號') // 消費者地址
    ->withItem('測試商品', quantity: 1, unit: '', price: 1000, amount: 1000) // 商品名稱、數量、單位、單價和金額
    ->withTax(TaxType::TAXABLE, 5) // 稅別:應稅稅別,稅率:5%
    ->withAmount(1000, 50, 1050) // 未稅銷售額、稅額、含稅銷售額
    ->issue();

開立 B2C 電子發票時,可以選擇使用不同的載具,如果設定了載具,會自動設定不索取紙本發票:

use Agriweather\EzPayInvoice\Enums\Invoice\CarrierType;

EzPayInvoice::invoice()
    ->create()
    ->forConsumer('John Doe')

    // 手機條碼載具: 第1碼 / + 7碼英、數字
    ->withCarrier(CarrierType::MOBILE, '/ABC.123')
    // 自然人憑證: 2碼大寫英文 + 14碼數字
    ->withCarrier(CarrierType::CITIZEN_CERT, 'AB12345678901234')
    // ezPay 電子發票載具
    ->withCarrier(CarrierType::EZPAY_CARRIER, '1234567890')
    ->withEmail('customer@example.com') // 當選擇 ezPay 電子發票載具時,需提供電子信箱

或者是可以提供捐贈碼,但不能與載具一起使用:

EzPayInvoice::invoice()
    ->create()
    ->forConsumer('John Doe')

    // 捐贈碼: 3~7 碼純數字
    ->withLoveCode('1234567')

開立 B2B 電子發票的基本範例:

$result = EzPayInvoice::invoice()
    ->create()
    ->withOrder('Order002')
    ->forBusiness('測試公司有限公司', '12345678') // 公司名稱、統一編號
    ->withEmail('business@company.com') // 公司電子信箱
    ->withAddress('台北市信義區信義路五段7號') // 公司地址
    ->withItem('商品A', quantity: 2, unit: '', price: 300, amount: 600) // 商品名稱、數量、單位、單價和金額
    ->withItem('商品B', quantity: 1, unit: '', price: 400, amount: 400) // 商品名稱、數量、單位、單價和金額
    ->withTax(TaxType::TAXABLE, 5) // 稅別:應稅稅別,稅率:5%
    ->withAmount(1000, 50, 1050) // 未稅銷售額、稅額、含稅銷售額
    ->issue();

如果開立了 B2B 電子發票,會自動設定索取紙本發票。

設定電子發票稅別和稅率:

use Agriweather\EzPayInvoice\Enums\Invoice\CustomsClearance;
use Agriweather\EzPayInvoice\Enums\Invoice\TaxType;

EzPayInvoice::invoice()
    ->create()

    // 應稅稅別,稅率:5%
    ->withTax(TaxType::TAXABLE, 5)

    // 零稅率
    ->withTax(TaxType::ZERO_RATE)
    // 當設定零稅率時,必須提供報關標記選項:
    ->withCustomsClearance(CustomsClearance::NON_CUSTOMS) // 非經海關
    ->withCustomsClearance(CustomsClearance::CUSTOMS) // 經海關

    // 免稅
    ->withTax(TaxType::TAX_FREE)

    // 混合應稅與免稅或零稅率 (例如:商品A應稅,商品B免稅)
    // 當開立 B2B 電子發票才能使用
    ->withTax(TaxType::MIXED)

設定電子發票的商品項目和銷售額:

Important

銷售額計算方式,請務必與公司財會人員進行確認。

EzPayInvoice::invoice()
    ->create()

    // 增加商品項目
    ->withItem('測試商品', quantity: 1, unit: '', price: 1000, amount: 1000)
    ->withItem('Test Product', quantity: 2, unit: 'EA', price: 600, amount: 1200)

    // 批次增加商品項目
    ->withItems([
        [
            'name' => '測試商品',
            'quantity' => 1,
            'unit' => '',
            'price' => 1000,
            'amount' => 1000,
        ],
        [
            'name' => 'Test Product',
            'quantity' => 2,
            'unit' => 'EA',
            'price' => 600,
            'amount' => 1200,
        ],
    ])

    // 未稅銷售額、稅額、含稅銷售額
    ->withAmount(2200, 110, 2310)

「混合應稅與免稅或零稅率」的銷售額設定範例:

Important

銷售額計算方式,請務必與公司財會人員進行確認。

use Agriweather\EzPayInvoice\Enums\Invoice\ItemTaxType;
use Agriweather\EzPayInvoice\Enums\Invoice\TaxType;

EzPayInvoice::invoice()
    ->create()
    ->forBusiness('測試公司有限公司', '12345678')

    // 設定稅別為混合應稅與免稅或零稅率
    ->withTax(TaxType::MIXED)

    // 為每個商品設定不同的稅別
    ->withItem('測試商品', quantity: 1, unit: '', price: 1000, amount: 1000, taxType: ItemTaxType::TAXABLE)
    ->withItem('測試商品', quantity: 1, unit: '', price: 600, amount: 600, taxType: ItemTaxType::ZERO_RATE)

    // 混合稅率銷售額: 銷售額(課稅別應稅)、銷售額(課稅別零稅率)、銷售額(課稅別免稅)
    ->withMixedTaxAmount(1000, 600, 0)

    // 銷售額為上面三個 混合稅率銷售額 的總和
    ->withAmount(1600, 0, 1600)

設定發票備註:

EzPayInvoice::invoice()
    ->create()
    ->withComment('發票備註'); // 發票備註,字數限 200 字,如有難字則再縮短

套件同時提供條件式語法,可以在開立發票時根據需要選擇性地添加參數:

use Agriweather\EzPayInvoice\Builders\Invoice\CreateBuilder;

EzPayInvoice::invoice()
    ->create()
    ->when($request->input('carrier_type') === 'mobile', function (CreateBuilder $builder) use ($mobileCarrier) {
        $builder->withCarrier(CarrierType::MOBILE, $mobileCarrier);
    })
    ->when($request->input('carrier_type') === 'lovecode', function (CreateBuilder $builder) use ($loveCode) {
        $builder->withLoveCode($loveCode);
    })

立即開立發票:

// 立即開立發票
$result = EzPayInvoice::invoice()
    ->create()
    ...
    ->issue();

$result->invoiceNumber() // 發票號碼:'AB12345678'
$result->randomNumber() // 發票隨機碼:'1234'
$result->orderNo() // 訂單編號:'Order001'
$result->totalAmount() // 含稅銷售額:1050

等待觸發開立發票:

// 等待觸發開立發票
// 當選擇此開立發票方式時,發票資料僅暫存於平台,需手動呼叫「觸發電子發票 API」來完成開立。
$result = EzPayInvoice::invoice()
    ->create()
    ...
    ->deferIssue();

// 預約自動開立發票
// 當選擇此開立發票方式時,發票會在設定時間自動開立,如需提前開立,可手動呼叫「觸發電子發票 API」。
$result = EzPayInvoice::invoice()
    ->create()
    ...
    ->scheduleAt('2025-03-01'); // 設定開立發票的時間

// 保存這些資料用於觸發開立發票
$result->invoiceTransNo() // ezPay 電子發票開立序號:'25072515224376654'
$result->orderNo() // 訂單編號:'Order001'
$result->totalAmount() // 含稅銷售額:1050

觸發開立電子發票

若使用了 等待觸發開立發票 (deferIssue()) 方式,需呼叫觸發電子發票 API 來完成開立。若使用 預約自動開立發票 (scheduleAt()) 方式,則可透過呼叫觸發 API 來提前開立發票:

$result = EzPayInvoice::invoice()
    ->pending()
    ->withInvoiceTransNo('25072515224376654')
    ->withOrder('Order004')
    ->withTotalAmount(210)
    ->trigger();

查詢電子發票

使用發票號碼和隨機碼查詢電子發票:

$invoiceResult = EzPayInvoice::invoice()
    ->query()
    ->withInvoice('GG72002017')
    ->withRandomNumber('1234')
    ->get();

或者也可使用訂單編號及發票金額查詢電子發票:

$invoiceResult = EzPayInvoice::invoice()
    ->query()
    ->withOrder('Order001')
    ->withTotalAmount(1050)
    ->get();

電子發票查詢結果包含以下資訊:

$invoiceResult->invoiceNumber() // 發票號碼:'GG72002017'
$invoiceResult->randomNumber() // 發票隨機碼:'1234'
$invoiceResult->orderNo() // 訂單編號:'Order001'
$invoiceResult->invoiceTransNo() // ezPay 電子發票開立序號:'25072515224376654'

$invoiceResult->invoiceStatus() // 發票狀態:`InvoiceStatus::ISSUED` (已開立)
$invoiceResult->invoiceUploadStatus() // 發票上傳財政部之狀態:`InvoiceUploadStatus::UPLOADED` (已上傳)

$invoiceResult->buyerName() // 買受人名稱:'John Doe'
$invoiceResult->buyerTaxIdNumber() // 買受人統一編號:'12345678'
$invoiceResult->buyerAddress() // 買受人地址:'台北市信義區信義路五段7號'
$invoiceResult->buyerPhone() // 買受人電話:'02-12345678'
$invoiceResult->buyerEmail() // 買受人電子信箱:'customer@example.com'

$invoiceResult->invoiceType() // 發票類別:`InvoiceType::GENERAL` (07: 一般稅額計算)
$invoiceResult->category() // 發票種類:`InvoiceCategory::B2C` (B2C電子發票)
$invoiceResult->taxType() // 課稅別:`TaxType::TAXABLE` (應稅)
$invoiceResult->taxRate() // 稅率:5.0 (5%)

$invoiceResult->amount() // 發票銷售額合計 (未稅):1000
$invoiceResult->salesAmount() // 銷售額 (課稅別應稅的未稅金額):300
$invoiceResult->zeroAmount() // 銷售額 (課稅別零稅率的未稅金額):350
$invoiceResult->freeAmount() // 銷售額 (課稅別免稅的未稅金額):300
$invoiceResult->taxAmount() // 稅額:50
$invoiceResult->totalAmount() // 含稅銷售額:1050

$invoiceResult->carrierType() // 載具類型:`CarrierType::MOBILE` (手機條碼載具)
$invoiceResult->carrierNumber() // 載具編號:'/ABC.123'
$invoiceResult->loveCode() // 捐贈碼:'1234567'
$invoiceResult->printFlag() // 是否索取紙本發票:true (索取紙本發票)
$invoiceResult->kioskPrintFlag() // 是否開放至合作超商 Kiosk 列印:true (開放列印)

$items = $invoiceResult->items() // 商品項目陣列
// [
//     [
//         'number' => 1,
//         'name' => '測試商品',
//         'quantity' => 1,
//         'unit' => '個',
//         'price' => 1000,
//         'amount' => 1000,
//         'taxType' => TaxType::TAXABLE, // 應稅
//     ],
//     [
//         'number' => 2,
//         'name' => 'Test Product',
//         'quantity' => 2,
//         'unit' => 'EA',
//         'price' => 600,
//         'amount' => 1200,
//         'taxType' => TaxType::ZERO_RATE, // 零稅率
//     ],
// ]

除了透過 API 查詢電子發票外,也可以直接跳轉到 ezPay 平台的查詢發票頁面:

Route::get('/invoice/search', function () {
    return EzPayInvoice::invoice()
        ->query()
        ->withInvoice('GG72002017')
        ->withRandomNumber('1234')
        ->redirectToEzPay();
});

或者是取得表單資料,並自行撰寫前端表單進行跳轉:

Tip

HTML 表單可以參考 src/Transporters/FormRedirectTransporter.php

$requestData = EzPayInvoice::invoice()
    ->query()
    ->withInvoice('GG72002017')
    ->withRandomNumber('1234')
    ->toRedirectRequestData();

// [
//     'url' => 'https://cinv.ezpay.com.tw/Api/invoice_issue',
//     'formData' => [
//         'MerchantID_' => 'your-merchant-id',
//         'PostData' => 'xxxxxx',
//     ],
// ]

或者是單純取得 ezPay 平台的查詢發票網址:

$url = EzPayInvoice::invoice()
    ->query()
    ->withInvoice('GG72002017')
    ->withRandomNumber('1234')
    ->getEzPaySearchUrl();

// 'https://inv.ezpay.com.tw/Invoice_index/search_platform?PostData=xxxxxx'

作廢電子發票

作廢電子發票需要傳入發票號碼和作廢原因:

EzPayInvoice::invoice()
    ->voidable()
    ->withInvoice('GG72002017')
    ->because('客戶取消訂單')
    ->invalidate();

開立折讓

當需要對已開立的電子發票進行部分或全部退貨時,可以立即開立發票折讓 (同時會立即確認折讓):

$result = EzPayInvoice::allowance()
    ->create()
    ->withInvoice('GG72002018')
    ->withOrder('Order001')
    ->withItem('退貨商品', quantity: 2, unit: '', price: 300, amount: 600, taxAmount: 30)
    ->withTotalAmount(630)
    ->withNotification('customer@example.com')
    ->issue();

$result->allowanceNo() // 折讓號:'A250725235346456'
$result->orderNo() // 訂單編號:'Order001'
$result->invoiceNumber() // 發票號碼:'GG72002018'
$result->allowanceAmount() // 折讓金額:630
$result->remainingAmount() // 折讓後剩餘發票金額:420

開立並延遲確認折讓

開立延遲確認的折讓,待買受人確認折讓後,再向 ezPay 平台發動確認折讓:

$result = EzPayInvoice::allowance()
    ->create()
    ...
    ->issuePendingConfirmation();

買受人發動確認折讓:

EzPayInvoice::allowance()
    ->pending()
    ->withAllowance('A250726001830959')
    ->withOrder('Order001')
    ->withTotalAmount(420)
    ->confirm();

買受人發動取消折讓:

EzPayInvoice::allowance()
    ->pending()
    ->withAllowance('A250726001830959')
    ->withOrder('Order001')
    ->withTotalAmount(420)
    ->cancel();

作廢折讓

作廢已開立的折讓,需傳入折讓號和作廢原因:

EzPayInvoice::allowance()
    ->voidable()
    ->withAllowance('A250726001830959')
    ->because('作廢原因')
    ->invalidate();

電子發票 API (境外電商版)

境外電商版的電子發票 API 主要差異在於部分欄位不同,不支援載具和捐贈碼,但增加了外幣、匯率等欄位,因此可在金額中輸入最多兩位小數。

境外電商開立電子發票

開立 B2C 電子發票的基本範例:

use Agriweather\EzPayInvoice\Enums\Invoice\CurrencyType;
use Agriweather\EzPayInvoice\Facades\EzPayInvoice;

$result = EzPayInvoice::crossBorder()
    ->invoice()
    ->create()
    ->withOrder('Order001') // 訂單編號
    ->withCustomer('John Doe') // 買受人姓名
    ->withEmail('customer@example.com') // 買受人電子信箱
    ->withAddress('台北市信義區信義路五段7號') // 買受人地址
    ->withCurrency(CurrencyType::USD) // 幣別:USD 美元
    ->withItem('國際商品', quantity: 1, unit: 'EA', price: 105.5, amount: 105.5) // 商品名稱、數量、單位、單價和金額
    ->withAmount(100.0, 5.5, 105.5) // 未稅銷售額、稅額、含稅銷售額
    ->withOriginalCurrencyAmount(100.0) // 營業人備註之原幣金額
    ->withExchangeRate(30.5) // 營業人備註之匯率
    ->issue();

設定電子發票使用幣別、原幣金額和匯率:

use Agriweather\EzPayInvoice\Enums\Invoice\CurrencyType;

$result = EzPayInvoice::crossBorder()
    ->invoice()
    ->create()

    ->withCurrency(CurrencyType::USD) // 幣別:USD 美元
    ->withOriginalCurrencyAmount(100.0) // 營業人備註之原幣金額
    ->withExchangeRate(30.5) // 營業人備註之匯率

ezPay 電子發票的幣別支援:

  • USD
  • HKD
  • GBP
  • AUD
  • CAD
  • SGD
  • CHF
  • JPY
  • ZAR
  • SEK
  • NZD
  • THB
  • PHP
  • IDR
  • EUR
  • KRW
  • VND
  • MYR
  • CNY
  • TWD

設定電子發票的商品項目和銷售額:

Important

銷售額計算方式,請務必與公司財會人員進行確認。

EzPayInvoice::crossBorder()
    ->invoice()
    ->create()

    // 增加商品項目
    ->withItem('測試商品', quantity: 1, unit: '', price: 105.5, amount: 105.5)
    ->withItem('Test Product', quantity: 2, unit: 'EA', price: 217.5, amount: 435)

    // 批次增加商品項目
    ->withItems([
        [
            'name' => '測試商品',
            'quantity' => 1,
            'unit' => '',
            'price' => 105.5,
            'amount' => 105.5,
        ],
        [
            'name' => 'Test Product',
            'quantity' => 2,
            'unit' => 'EA',
            'price' => 217.5,
            'amount' => 435,
        ],
    ])

    // 未稅銷售額、稅額、含稅銷售額
    ->withAmount(520.0, 20.5, 540.5)

設定發票備註:

EzPayInvoice::crossBorder()
    ->invoice()
    ->create()
    ->withComment('發票備註'); // 發票備註,字數限 200 字,如有難字則再縮短

立即開立發票:

// 立即開立發票
$result = EzPayInvoice::crossBorder()
    ->invoice()
    ->create()
    ...
    ->issue();

$result->invoiceNumber() // 發票號碼:'AB12345678'
$result->randomNumber() // 發票隨機碼:'1234'
$result->orderNo() // 訂單編號:'Order001'
$result->totalAmount() // 含稅銷售額:105.5

等待觸發開立發票:

// 等待觸發開立發票
// 當選擇此開立發票方式時,發票資料僅暫存於平台,需手動呼叫「觸發電子發票 API」來完成開立。
$result = EzPayInvoice::crossBorder()
    ->invoice()
    ->create()
    ...
    ->deferIssue();

// 預約自動開立發票
// 當選擇此開立發票方式時,發票會在設定時間自動開立,如需提前開立,可手動呼叫「觸發電子發票 API」。
$result = EzPayInvoice::crossBorder()
    ->invoice()
    ->create()
    ...
    ->scheduleAt('2025-03-01'); // 設定開立發票的時間

// 保存這些資料用於觸發開立發票
$result->invoiceTransNo() // ezPay 電子發票開立序號:'25072515224376654'
$result->orderNo() // 訂單編號:'Order001'
$result->totalAmount() // 含稅銷售額:105.5

境外電商觸發開立電子發票

若使用了 等待觸發開立發票 (deferIssue()) 方式,需呼叫觸發電子發票 API 來完成開立。若使用 預約自動開立發票 (scheduleAt()) 方式,則可透過呼叫觸發 API 來提前開立發票:

$result = EzPayInvoice::crossBorder()
    ->invoice()
    ->pending()
    ->withInvoiceTransNo('25072515224376654')
    ->withOrder('Order004')
    ->withTotalAmount(217.5)
    ->trigger();

境外電商查詢電子發票

使用發票號碼和隨機碼查詢電子發票:

$invoiceResult = EzPayInvoice::crossBorder()
    ->invoice()
    ->query()
    ->withInvoice('GG72002017')
    ->withRandomNumber('1234')
    ->get();

或者也可使用訂單編號及發票金額查詢電子發票:

$invoiceResult = EzPayInvoice::crossBorder()
    ->invoice()
    ->query()
    ->withOrder('Order001')
    ->withTotalAmount(105.5)
    ->get();

電子發票查詢結果包含以下資訊:

$invoiceResult->invoiceNumber() // 發票號碼:'GG72002017'
$invoiceResult->randomNumber() // 發票隨機碼:'1234'
$invoiceResult->orderNo() // 訂單編號:'Order001'
$invoiceResult->invoiceTransNo() // ezPay 電子發票開立序號:'25072515224376654'

$invoiceResult->invoiceStatus() // 發票狀態:`InvoiceStatus::ISSUED` (已開立)
$invoiceResult->invoiceUploadStatus() // 發票上傳財政部之狀態:`InvoiceUploadStatus::UPLOADED` (已上傳)

$invoiceResult->buyerName() // 買受人名稱:'John Doe'
$invoiceResult->buyerAddress() // 買受人地址:'台北市信義區信義路五段7號'
$invoiceResult->buyerEmail() // 買受人電子信箱:'customer@example.com'

$invoiceResult->invoiceType() // 發票類別:`InvoiceType::GENERAL` (07: 一般稅額計算)

$invoiceResult->currency() // 幣別:`CurrencyType::USD` (USD 美元)
$invoiceResult->originalCurrencyAmount() // 營業人備註之原幣金額:100.0
$invoiceResult->exchangeRate() // 營業人備註之匯率:30.5

$invoiceResult->amount() // 發票銷售額合計 (未稅):100.0
$invoiceResult->taxAmount() // 稅額:5.5
$invoiceResult->totalAmount() // 含稅銷售額:105.5

$items = $invoiceResult->items() // 商品項目陣列
// [
//     [
//         'number' => 1,
//         'name' => '測試商品',
//         'quantity' => 1,
//         'unit' => '個',
//         'price' => 105.5,
//         'amount' => 105.5,
//     ],
//     [
//         'number' => 2,
//         'name' => 'Test Product',
//         'quantity' => 2,
//         'unit' => 'EA',
//         'price' => 217.5,
//         'amount' => 435,
//     ],
// ]

除了透過 API 查詢電子發票外,也可以直接跳轉到 ezPay 平台的查詢發票頁面:

Route::get('/invoice/search', function () {
    return EzPayInvoice::crossBorder()
        ->invoice()
        ->query()
        ->withInvoice('GG72002017')
        ->withRandomNumber('1234')
        ->redirectToEzPay();
});

或者是取得表單資料,並自行撰寫前端表單進行跳轉:

Tip

HTML 表單可以參考 src/Transporters/FormRedirectTransporter.php

$requestData = EzPayInvoice::invoice()
    ->query()
    ->withInvoice('GG72002017')
    ->withRandomNumber('1234')
    ->toRedirectRequestData();

// [
//     'url' => 'https://cinv.ezpay.com.tw/Api/invoice_issue',
//     'formData' => [
//         'MerchantID_' => 'your-merchant-id',
//         'PostData' => 'xxxxxx',
//     ],
// ]

或者是單純取得 ezPay 平台的查詢發票網址:

$url = EzPayInvoice::crossBorder()
    ->invoice()
    ->query()
    ->withInvoice('GG72002017')
    ->withRandomNumber('1234')
    ->getEzPaySearchUrl();

// 'https://inv.ezpay.com.tw/Invoice_index/search_platform?PostData=xxxxxx'

境外電商作廢電子發票

作廢電子發票需要傳入發票號碼和作廢原因:

EzPayInvoice::crossBorder()
    ->invoice()
    ->voidable()
    ->withInvoice('GG72002017')
    ->because('客戶取消訂單')
    ->invalidate();

境外電商開立折讓

當需要對已開立的電子發票進行部分或全部退貨時,可以立即開立發票折讓 (同時會立即確認折讓):

$result = EzPayInvoice::crossBorder()
    ->allowance()
    ->create()
    ->withInvoice('GG72002018')
    ->withOrder('Order001')
    ->withItem('退貨商品', quantity: 2, unit: '', price: 217.5, amount: 435)
    ->withTotalAmount(435)
    ->withNotification('customer@example.com')
    ->issue();

$result->allowanceNo() // 折讓號:'A250725235346456'
$result->orderNo() // 訂單編號:'Order001'
$result->invoiceNumber() // 發票號碼:'GG72002018'
$result->allowanceAmount() // 折讓金額:435
$result->remainingAmount() // 折讓後剩餘發票金額:105.5

境外電商開立並延遲確認折讓

開立延遲確認的折讓,待買受人確認折讓後,再向 ezPay 平台發動確認折讓:

$result = EzPayInvoice::crossBorder()
    ->allowance()
    ->create()
    ...
    ->issuePendingConfirmation();

買受人發動確認折讓:

EzPayInvoice::crossBorder()
    ->allowance()
    ->pending()
    ->withAllowance('A250726001830959')
    ->withOrder('Order001')
    ->withTotalAmount(435)
    ->confirm();

買受人發動取消折讓:

EzPayInvoice::crossBorder()
    ->allowance()
    ->pending()
    ->withAllowance('A250726001830959')
    ->withOrder('Order001')
    ->withTotalAmount(435)
    ->cancel();

境外電商作廢折讓

作廢已開立的折讓,需傳入折讓號和作廢原因:

EzPayInvoice::crossBorder()
    ->allowance()
    ->voidable()
    ->withAllowance('A250726001830959')
    ->because('作廢原因')
    ->invalidate();

字軌管理 API

準備帳號代號和金鑰

字軌管理和開立電子發票需要不同的帳號代號和金鑰。首先前往 ezPay 電子發票平台的「會員管理」頁面,找到並複製會員編號、會員 API 串接金鑰的 HashKeyHashIV,然後將這些資訊設定到 .env 檔案中的 EZPAY_INVOICE_COMPANY_ID 等參數:

EZPAY_INVOICE_COMPANY_ID=your-company-id
EZPAY_INVOICE_COMPANY_HASH_KEY=your-company-hash-key
EZPAY_INVOICE_COMPANY_HASH_IV=your-company-hash-iv

申請新字軌

使用新增字軌 API 申請新字軌:

use Agriweather\EzPayInvoice\Enums\Invoice\InvoiceTerm;
use Agriweather\EzPayInvoice\Enums\Invoice\InvoiceType;
use Agriweather\EzPayInvoice\Facades\EzPayInvoice;

$result = EzPayInvoice::alphanumericCode()
    ->create()
    ->withYear(113) // 民國年,只可輸入今年與明年。
    ->withTerm(InvoiceTerm::JUL_AUG) // 發票期別:07-08月
    ->withCode('AA') // 字軌英文代碼
    ->withRange('24000100', '24000199') // 發票號碼範圍
    ->withType(InvoiceType::GENERAL) // 發票類別:`InvoiceType::GENERAL` (07: 一般稅額計算)
    ->save();

$result->managementNo() // 字軌管理編號:'0t0ghr0fyv'
$result->lastNumber() // 該組字軌剩餘張數:100
$result->status() // 字軌狀態: `AlphanumericCodeStatus::ACTIVE` (啟用)

查詢字軌

使用發票年度和期別來查詢字軌資訊:

$alphanumericCodeResults = EzPayInvoice::alphanumericCode()
    ->query()
    ->withYear(113)
    ->withTerm(InvoiceTerm::JUL_AUG)
    ->get();

foreach ($alphanumericCodeResults as $result) {
    $result->managementNo() // 字軌管理編號:'0t0ghr0fyv'
    $result->year() // 發票年度:113
    $result->term() // 發票期別:`InvoiceTerm::JUL_AUG` (07-08月)
    $result->alphanumericCode() // 字軌英文代碼:'AA'
    $result->startNumber() // 字軌起號:'24000100'
    $result->endNumber() // 字軌迄號:'24000199'
    $result->lastNumber() // 該組字軌剩餘張數:100
    $result->type() // 發票類別:`InvoiceType::GENERAL` (07: 一般稅額計算)
    $result->status() // 字軌狀態: `AlphanumericCodeStatus::ACTIVE` (啟用)
}

啟用字軌

如果字軌是暫停狀態,可以啟用字軌。需要傳入字軌管理編號和發票年度:

EzPayInvoice::alphanumericCode()
    ->query()
    ->withNo('0t0ghr0fyv')
    ->withYear(113)
    ->enable();

暫停字軌

如果字軌是啟用狀態,可以暫停字軌。需要傳入字軌管理編號和發票年度:

$result = EzPayInvoice::alphanumericCode()
    ->query()
    ->withNo('0t0ghr0fyv')
    ->withYear(113)
    ->pause();

停用字軌

可以停用字軌,但請注意,停用後將無法再啟用該字軌。需要傳入字軌管理編號和發票年度:

EzPayInvoice::alphanumericCode()
    ->query()
    ->withNo('0t0ghr0fyv')
    ->withYear(113)
    ->disable();

手機條碼與捐證碼驗證 API

驗證手機條碼

驗證手機條碼是否存在於財政部電子發票整合服務平台:

use Agriweather\EzPayInvoice\Facades\EzPayInvoice;

$result = EzPayInvoice::codeValidation()
    ->withBarcode('/ABC.123')
    ->check();

$result->isValid() // 手機條碼是否有效:true

驗證捐證碼

驗證捐證碼是否存在於財政部電子發票整合服務平台:

use Agriweather\EzPayInvoice\Facades\EzPayInvoice;

$result = EzPayInvoice::codeValidation()
    ->withLoveCode('123')
    ->check();

$result->isValid() // 捐證碼是否有效:true

錯誤處理

當 ezPay API 回傳失敗的回應時,會拋出 EzPayInvoiceException 例外,可以取得 ezPay 的錯誤代碼和錯誤訊息進行進一步處理:

use Agriweather\EzPayInvoice\Exceptions\EzPayInvoiceException;

try {
    $result = EzPayInvoice::invoice()
        ->create()
        ...
        ->issue();
} catch (EzPayInvoiceException $e) {
    $status = $e->getApiStatus(); // 'KEY10013'
    $message = $e->getApiMessage(); // '資料不可空白MerchantOrderNo'

    // 記錄錯誤日誌...
    logger()->error($e->getMessage(), $e->context());

    // 顯示錯誤訊息給使用者...
    return response()->json([
        'error' => $message,
    ], 400);
}

單元測試

在單元測試中,可以使用 EzPayInvoice::fake() 模擬 API 回應,這邊要模擬開立發票回應,因此使用 CreateResult 來建立模擬回應資料:

<?php

use Agriweather\EzPayInvoice\Enums\Invoice\InvoiceCategory;
use Agriweather\EzPayInvoice\Enums\Invoice\TaxType;
use Agriweather\EzPayInvoice\Facades\EzPayInvoice;
use Agriweather\EzPayInvoice\Resources\Invoice;
use Agriweather\EzPayInvoice\Results\Invoice\CreateResult;

test('can create invoice', function () {
    // 模擬發票開立 API 回應
    EzPayInvoice::fake([
        // 模擬開立發票回應
        // 需要使用實際呼叫的 Result 類別來建立模擬回應
        //
        // 因為下面使用 EzPayInvoice::invoice()->create()->issue() 來開立發票
        // 因此模擬的回應類別需要使用 CreateResult 類別
        CreateResult::make([
            'Status' => 'SUCCESS',
            'Message' => '發票開立成功',
            'Result' => [
                'CheckCode' => '123456789',
                'MerchantID' => '111335678',
                'MerchantOrderNo' => 'Order001',
                'InvoiceNumber' => 'GG72002017',
                'TotalAmt' => 1050,
                'InvoiceTransNo' => '25072515224376654',
                'RandomNum' => '1234',
                'CreateTime' => '2025-01-01 00:00:00',
            ],
        ]),
    ]);

    // 測試發送開立發票請求
    $response = $this->post('/invoice/create', [
        'category' => 'B2C',
        'name' => 'John Doe',
        'email' => 'customer@example.com',
        'items' => [
            [
                'name' => '測試商品',
                'quantity' => 1,
                'unit' => '',
                'price' => 1000,
                'amount' => 1000,
            ],
        ],
        'total_amount' => 1050,
    ]);

    // 斷言發送參數
    //
    // 因為上面是呼叫 EzPayInvoice::invoice()->create()
    //
    // 因此斷言的資源類別和方法名稱分別是:
    // - Invoice::class 是 invoice() 方法回傳的資源類別
    // - 'create' 是呼叫的方法名稱
    EzPayInvoice::assertSent(Invoice::class, 'create', function (CreateOptions $options) {
        return $options->orderNo === 'Order001'
            && $options->category === InvoiceCategory::B2C
            && $options->buyerName === 'John Doe'
            && $options->buyerEmail === 'customer@example.com'
            && $options->hasItem('測試商品', quantity: 1, unit: '', price: 1000, amount: 1000)
            && $options->taxType === TaxType::TAXABLE
            && $options->taxRate === 5.0
            && $options->totalAmount === 1050;
    });
});

Warning

不支援模擬查詢發票的 redirectToEzPay()toRedirectRequestData() 方法,因為這兩個方法是直接產生跳轉表單資料,實際上不會發送 API 請求。

除錯支援

當發生錯誤時,請協助提供請求與回應的除錯資料,以便更快速地定位問題:

use Agriweather\EzPayInvoice\Options\Options;

$result = EzPayInvoice::invoice()
    ->create()
    ->withOrder('Order001')
    ...
    ->onPreparedOptions(function (Options $options) {
        dd($options->toArray()); // 查看請求參數資料
    })
    ->issue();

dd($result->toArray()); // 查看回應資料
  • 使用 $options->toArray() 方法可檢視發送至 API 的請求資料
  • 透過 $result->toArray() 可檢視 API 的回應資料內容
  • 當發生錯誤時,請在提交 issue 時一併提供請求與回應的除錯資料

API 參考文件

ezPay 電子發票 API 文件下載專區

當前參考文件版本:

  • 電子發票API v1.2.2 (2024/05/09)
  • 電子發票API_境外電商版 v1.0.0 (2021/02/02)
  • 字軌管理API v1.0.0 (2018/10/08)
  • 手機條碼與捐證碼驗證技術串接手冊 v1.0.0 (2021/03/03)

貢獻專案

歡迎參與貢獻專案,請參考 貢獻指南 文件。

License

基於 MIT LICENSE 釋出