PHP code example of dennykuo / invoice-porter

1. Go to this page and download the library: Download dennykuo/invoice-porter library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

dennykuo / invoice-porter example snippets


use InvoicePorter\Ezpay\Environment;
use InvoicePorter\Ezpay\EzpayConfig;

new EzpayConfig(
    merchantId: 'YOUR_MERCHANT_ID',
    hashKey: 'YOUR_HASH_KEY_32_CHARS_xxxxxxxxxxx',  // 必須 32 字元
    hashIv: 'YOUR_IV_16_CHARS',                     // 必須 16 字元
    environment: Environment::Sandbox,              // 或 Environment::Production
    timeoutSeconds: 10.0,                           // 預設 10 秒
    connectTimeoutSeconds: 5.0,                     // 預設 5 秒
);

EzpayConfig::fromEnv();              // 讀 EZPAY_MERCHANT_ID / HASH_KEY / HASH_IV / ENVIRONMENT
EzpayConfig::fromEnv('VENDOR_A_');   // 讀 VENDOR_A_MERCHANT_ID …(適合一個系統多個藍新帳號)

// config/ezpay.php
return [
    'merchant_id'             => env('EZPAY_MERCHANT_ID'),
    'hash_key'                => env('EZPAY_HASH_KEY'),
    'hash_iv'                 => env('EZPAY_HASH_IV'),
    'environment'             => env('EZPAY_ENVIRONMENT', 'sandbox'),
    'timeout_seconds'         => 10.0,
    'connect_timeout_seconds' => 5.0,
    // 以下三欄為字軌 API 才需要
    'company_id'              => env('EZPAY_COMPANY_ID'),
    'company_hash_key'        => env('EZPAY_COMPANY_HASH_KEY'),
    'company_hash_iv'         => env('EZPAY_COMPANY_HASH_IV'),
];

// AppServiceProvider 之類
$config = EzpayConfig::fromArray(config('ezpay'));

use InvoicePorter\Ezpay\EzpayInvoiceClient;
use InvoicePorter\Ezpay\Enums\Category;
use InvoicePorter\Ezpay\Enums\InvoiceStatus;
use InvoicePorter\Ezpay\Enums\TaxType;
use InvoicePorter\Ezpay\Requests\InvoiceIssueRequest;
use InvoicePorter\Ezpay\Requests\Items\InvoiceItem;

$client = new EzpayInvoiceClient(EzpayConfig::fromEnv());

$response = $client->issue(new InvoiceIssueRequest(
    status: InvoiceStatus::Immediate,
    merchantOrderNo: 'ORD_' . date('YmdHis'),
    category: Category::B2C,
    taxType: TaxType::Taxable,
    amount: 476,
    taxAmount: 24,
    totalAmount: 500,
    items: [
        new InvoiceItem(name: '商品一', count: 1, unit: '個', price: 500, amount: 500),
    ],
));

echo $response->invoiceNumber();

use InvoicePorter\Ezpay\Enums\InvoiceTerm;
use InvoicePorter\Ezpay\EzpayConfig;
use InvoicePorter\Ezpay\EzpayTrackClient;
use InvoicePorter\Ezpay\Requests\Track\TrackCreateRequest;

$config = new EzpayConfig(
    merchantId: 'YOUR_MERCHANT_ID',
    hashKey: 'YOUR_HASH_KEY_32_CHARS_xxxxxxxxxxx',
    hashIv: 'YOUR_IV_16_CHARS',
    companyId: 'YOUR_COMPANY_ID',                              // 字軌專用
    companyHashKey: 'YOUR_COMPANY_HASH_KEY_32_xxxxxxxxxxxxx',  // 字軌專用
    companyHashIv: 'YOUR_COMPANY_IV',                          // 字軌專用
);

$client = new EzpayTrackClient($config);

$response = $client->trackCreate(new TrackCreateRequest(
    year: '115',                // 民國年三碼
    term: InvoiceTerm::JanFeb,  // 1=一二月、2=三四月、…
    aphabeticLetter: 'AB',      // 字軌字母(兩碼大寫)
    startNumber: '00000000',    // 起號 8 碼
    endNumber: '00000049',      // 訖號 8 碼
));

echo $response->managementNo();  // 新增成功後的字軌管理編號

// 觸發開立(先以 InvoiceStatus::Pending 建單後,呼叫此 API 才實際開立)
$client->touchIssue(new InvoiceTouchIssueRequest(
    merchantOrderNo: 'ORD_20260504_001',
    totalAmount: 500,
));

// 作廢發票(最小欄位)
$client->invalid(new InvoiceInvalidRequest(
    invoiceNumber: 'AA00000076',
    invalidReason: '訂單取消',
));

// 開立折讓
$client->issueAllowance(new AllowanceIssueRequest(
    invoiceNo: 'AA00000076',
    merchantOrderNo: 'ORD_20260504_001',
    totalAmount: 100,
    taxAmount: 5,
    items: [new AllowanceItem(name: '商品一', count: 1, unit: '個', price: 95, amount: 95, taxAmount: 5)],
));

// 確認 / 取消折讓
$client->touchAllowance(new AllowanceTouchIssueRequest(
    allowanceNo: 'A001',
    status: AllowanceTouchStatus::Confirm,  // 或 ::Deny(取消)
));

// 作廢折讓
$client->invalidAllowance(new AllowanceInvalidRequest(
    allowanceNo: 'A001',
    invalidReason: '客戶取消',
));

// 用發票號碼查詢(必須帶 randomNum)
$response = $client->search(new InvoiceSearchRequest(
    searchType: SearchType::ByInvoiceNumber,
    merchantOrderNo: 'ORD_20260504_001',
    invoiceNumber: 'AA00000076',
    randomNum: '0991',
));

echo $response->lifecycleStatus()?->value;  // 1=已開立、2=已作廢

new InvoiceIssueRequest(
    status: InvoiceStatus::Immediate,
    merchantOrderNo: 'ORD_...',
    category: Category::B2B,
    taxType: TaxType::Taxable,
    amount: 476, taxAmount: 24, totalAmount: 500,
    buyerName: '王大公司',
    buyerUbn: '12345678',  // B2B 必填,否則 EzpayValidationException
    items: [new InvoiceItem(...)],
);

new InvoiceIssueRequest(
    // ...
    carrierType: CarrierType::Mobile,    // 或 ::CitizenDigitalCertificate / ::Member
    carrierNum: '/ABC1234',              // 手機條碼以 / 開頭
);

new InvoiceIssueRequest(
    // ...
    loveCode: '13994',  // 不可與 carrierType 同時使用
);

new InvoiceIssueRequest(
    status: InvoiceStatus::Scheduled,
    createStatusTime: '2026-06-01',  // YYYY-MM-DD,必填
    // ...
);

// 混合稅率時,每個 item 必須給 taxType('1' 應稅、'2' 零稅、'3' 免稅)
new InvoiceIssueRequest(
    taxType: TaxType::Mixed,
    items: [
        new InvoiceItem(name: '應稅商品', count: 1, unit: '個', price: 100, amount: 100, taxType: '1'),
        new InvoiceItem(name: '免稅商品', count: 1, unit: '個', price: 50,  amount: 50,  taxType: '3'),
    ],
    // ...
);

$response->isSuccess();    // bool — 等同 status() === 'SUCCESS'
$response->status();       // string — 'SUCCESS' 或業務錯誤碼
$response->message();      // string — 藍新原始訊息
$response->rawResponse();  // array  — 完整 envelope,方便寫 log

$response->createTime();    // ?string — 藍新原始 'Y-m-d H:i:s'
$response->createTimeAt();  // ?DateTimeImmutable — 解析後物件,省去自行 parse

use InvoicePorter\Ezpay\Exceptions\EzpayApiException;
use InvoicePorter\Ezpay\Exceptions\EzpayException;

try {
    $response = $client->issue($request);
} catch (EzpayApiException $e) {
    // 業務錯誤:可拿到 errorCode / message / rawResponse
    log_business_error($e->errorCode, $e->getMessage());
} catch (EzpayException $e) {
    // 其餘 SDK 錯誤
    log_sdk_error($e);
}

try {
    $response = $client->issue($request);
} catch (EzpayApiException $e) {
    if ($e->isDuplicateOrderNo()) {
        // NOR10001 / LIB10003 → 產生新訂單編號後重新建 Request 重試
        return retry_with_new_order_no();
    }
    if ($e->isAuthError()) {
        // INV900xx / KEY100xx → 憑證或解密問題,告警 ops 修設定
        alert_ops($e);
        throw $e;
    }
    if ($e->isFieldFormatError()) {
        // INV100xx / INV700xx → 欄位格式錯,引導使用者修正
        return show_validation_error($e);
    }
    throw $e;
}

// 作廢發票 — 帶齊 4 個欄位即啟用 CheckCode 驗證
$client->invalid(new InvoiceInvalidRequest(
    invoiceNumber: 'AA00000076',
    invalidReason: '訂單取消',
    randomNum: '0991',
    invoiceTransNo: '24050414461511234',
    merchantOrderNo: 'ORD_20260504_001',
    totalAmount: 500,
));

// 折讓系列 — 明確開啟驗證
$client->issueAllowance(new AllowanceIssueRequest(
    // ...
    expectCheckCode: true,
));

  echo $client->searchRedirectHtml($request);
  

  $response = $client->search($request);
  $url = $response->searchResultUrl();
  

// A:直接給四個欄位,不必先組 Request
echo $client->publicQueryRedirectHtml(
    invoiceNumber: 'AA00000076',
    randomNum: '0991',
    merchantOrderNo: 'ORD_20260504_001',
    totalAmount: 500,
);

// B:剛 issue 完,直接從 Response 產生 Request 再丟回 searchRedirectHtml()
$response = $client->issue($issueRequest);
echo $client->searchRedirectHtml($response->toSearchRequest());

use InvoicePorter\Exceptions\InvoiceException;

try {
    $response = $client->issue($request);
} catch (InvoiceException $e) {
    // 不論藍新或未來其他廠商,這裡都會接到
}
bash
cp examples/.env.example examples/.env
# 編輯 examples/.env 填入您自己的測試憑證
php examples/01-issue.php