PHPackages                             carllee1983/ecpay-logistics - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. carllee1983/ecpay-logistics

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

carllee1983/ecpay-logistics
===========================

綠界科技物流整合 PHP SDK

v1.0.0(5mo ago)00MITPHPPHP ^8.3

Since Nov 27Pushed 5mo agoCompare

[ Source](https://github.com/CarlLee1983/ecpay-logistics)[ Packagist](https://packagist.org/packages/carllee1983/ecpay-logistics)[ RSS](/packages/carllee1983-ecpay-logistics/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (6)Versions (2)Used By (0)

ECPay Logistics SDK
===================

[](#ecpay-logistics-sdk)

綠界科技物流整合 PHP SDK

安裝
--

[](#安裝)

```
composer require carllee1983/ecpay-logistics
```

環境需求
----

[](#環境需求)

- PHP 8.3+
- OpenSSL 擴展
- JSON 擴展
- TLS 1.2 支援（綠界僅支援 TLS 1.2 加密通訊協定）

重要注意事項
------

[](#重要注意事項)

### 安全性警告

[](#安全性警告)

- **請勿將 HashKey/HashIV 存放或顯示於前端網頁**（如 JavaScript、HTML、CSS），避免金鑰被盜取造成損失及資料外洩
- 務必透過環境變數或設定檔管理金鑰，確保不納入版本控制

### API 呼叫注意事項

[](#api-呼叫注意事項)

- 所有 API 使用 **HTTP POST** 方式傳送
- 資料傳遞格式採用 **Form-data** 及 **MD5** 加密機制
- 請進行主機**時間校正**，避免時差導致 API 無法正常運作

支援的物流類型
-------

[](#支援的物流類型)

### 超商物流

[](#超商物流)

類型說明超商C2C店到店7-ELEVEN、全家、萊爾富、OK超商B2C大宗寄倉7-ELEVEN、全家、萊爾富### 宅配服務

[](#宅配服務)

類型說明TCAT黑貓宅急便POST中華郵政操作類別對應
------

[](#操作類別對應)

### 門市相關 (StoreMap)

[](#門市相關-storemap)

類別別名說明`OpenStoreMap``store_map`門市電子地圖### 超商物流 (Cvs)

[](#超商物流-cvs)

類別別名說明`CreateCvsOrder``cvs.create`建立超商訂單`UpdateCvsOrder``cvs.update`異動超商訂單`CancelCvsOrder``cvs.cancel`取消訂單（C2C 7-11）`ReturnCvsOrder``cvs.return`B2C 逆物流### 宅配物流 (Home)

[](#宅配物流-home)

類別別名說明`CreateHomeOrder``home.create`建立宅配訂單`ReturnHomeOrder``home.return`宅配逆物流### 查詢 (Queries)

[](#查詢-queries)

類別別名說明`QueryLogisticsOrder``queries.order`查詢物流訂單`GetStoreList``queries.store_list`取得門市清單### 列印 (Printing)

[](#列印-printing)

類別別名說明`PrintTradeDocument``printing.trade`B2C/宅配列印`PrintCvsDocument``printing.cvs`C2C 列印### 通知處理 (Notifications)

[](#通知處理-notifications)

類別說明`LogisticsNotify`物流狀態通知`ReverseLogisticsNotify`逆物流狀態通知設定
--

[](#設定)

### 環境變數

[](#環境變數)

```
ECPAY_LOGISTICS_SERVER=https://logistics-stage.ecpay.com.tw
ECPAY_LOGISTICS_MERCHANT_ID=your_merchant_id
ECPAY_LOGISTICS_HASH_KEY=your_hash_key
ECPAY_LOGISTICS_HASH_IV=your_hash_iv
```

### Laravel 整合

[](#laravel-整合)

發布設定檔：

```
php artisan vendor:publish --provider="CarlLee\EcPayLogistics\Laravel\EcPayLogisticsServiceProvider"
```

基本用法
----

[](#基本用法)

### 門市電子地圖

[](#門市電子地圖)

讓消費者選擇取貨門市：

```
use CarlLee\EcPayLogistics\Factories\OperationFactory;
use CarlLee\EcPayLogistics\FormBuilder;
use CarlLee\EcPayLogistics\Parameter\LogisticsType;
use CarlLee\EcPayLogistics\Parameter\LogisticsSubType;

$factory = new OperationFactory([
    'merchant_id' => 'your_merchant_id',
    'hash_key' => 'your_hash_key',
    'hash_iv' => 'your_hash_iv',
]);

$storeMap = $factory->make('store_map')
    ->setMerchantTradeNo('ORDER_' . time())
    ->setLogisticsType(LogisticsType::CVS)
    ->setLogisticsSubType(LogisticsSubType::UNIMART_C2C)
    ->setIsCollection('N')
    ->setServerReplyURL('https://your-domain.com/store-callback');

$formBuilder = new FormBuilder('https://logistics-stage.ecpay.com.tw');
echo $formBuilder->autoSubmit($storeMap);
```

### 建立超商物流訂單

[](#建立超商物流訂單)

```
$order = $factory->make('cvs.create')
    ->setMerchantTradeNo('ORDER_' . time())
    ->setMerchantTradeDate(date('Y/m/d H:i:s'))
    ->setLogisticsType(LogisticsType::CVS)
    ->setLogisticsSubType(LogisticsSubType::UNIMART_C2C)
    ->setGoodsAmount(1000)
    ->setGoodsName('測試商品')
    ->setSenderName('寄件人')
    ->setSenderCellPhone('0912345678')
    ->setReceiverName('收件人')
    ->setReceiverCellPhone('0987654321')
    ->setReceiverStoreID('991182')  // 門市代號
    ->setServerReplyURL('https://your-domain.com/logistics-callback');

$response = $order->send();
```

### 建立宅配訂單

[](#建立宅配訂單)

```
use CarlLee\EcPayLogistics\Parameter\LogisticsSubType;
use CarlLee\EcPayLogistics\Parameter\Temperature;
use CarlLee\EcPayLogistics\Parameter\Distance;
use CarlLee\EcPayLogistics\Parameter\Specification;

$order = $factory->make('home.create')
    ->setMerchantTradeNo('HOME_' . time())
    ->setLogisticsType(LogisticsType::HOME)
    ->setLogisticsSubType(LogisticsSubType::TCAT)
    ->setGoodsAmount(2000)
    ->setGoodsName('測試商品')
    ->setSenderName('寄件人')
    ->setSenderPhone('02-12345678')
    ->setSenderCellPhone('0912345678')
    ->setSenderZipCode('106')
    ->setSenderAddress('台北市大安區忠孝東路100號')
    ->setReceiverName('收件人')
    ->setReceiverPhone('03-12345678')
    ->setReceiverCellPhone('0987654321')
    ->setReceiverZipCode('320')
    ->setReceiverAddress('桃園市中壢區中正路200號')
    ->setTemperature(Temperature::ROOM)
    ->setDistance(Distance::SAME)
    ->setSpecification(Specification::SIZE_60)
    ->setServerReplyURL('https://your-domain.com/logistics-callback');

$response = $order->send();
```

### 處理物流狀態通知

[](#處理物流狀態通知)

```
use CarlLee\EcPayLogistics\Notifications\LogisticsNotify;

$notify = new LogisticsNotify($hashKey, $hashIV);

if ($notify->verify($_POST)) {
    $logisticsId = $notify->getAllPayLogisticsID();
    $status = $notify->getRtnCode();

    // 更新訂單物流狀態

    // 重要：必須回傳 1|OK 給綠界
    echo $notify->getSuccessResponse(); // 1|OK
}
```

### 查詢物流訂單

[](#查詢物流訂單)

```
$query = $factory->make('queries.order')
    ->setAllPayLogisticsID('1234567890');

$response = $query->send();
```

前端框架整合（Vue / React / Next.js）
-----------------------------

[](#前端框架整合vue--react--nextjs)

由於綠界物流的門市電子地圖需要透過表單 POST 提交，在現代前端框架（SPA）中需要特別處理。以下提供幾種整合方式：

### 後端 API 設計

[](#後端-api-設計)

首先，建立後端 API 端點來產生表單資料：

```
// Laravel 範例：routes/api.php
Route::post('/logistics/store-map', function (Request $request) {
    $factory = app('ecpay.logistics');

    $storeMap = $factory->make('store_map')
        ->setMerchantTradeNo('ORDER_' . time())
        ->setLogisticsSubType(LogisticsSubType::from($request->input('sub_type', 'UNIMARTC2C')))
        ->setIsCollection($request->input('is_collection', 'N'))
        ->setServerReplyURL(config('app.url') . '/api/logistics/store-callback');

    $formBuilder = new FormBuilder(config('ecpay-logistics.server'));

    return response()->json([
        'action' => $formBuilder->getActionUrl($storeMap),
        'fields' => $formBuilder->getFields($storeMap),
    ]);
});

// 門市選擇回調（綠界會 POST 到此端點）
Route::post('/logistics/store-callback', function (Request $request) {
    // 儲存門市資訊到 Session 或快取
    $storeData = [
        'CVSStoreID' => $request->input('CVSStoreID'),
        'CVSStoreName' => $request->input('CVSStoreName'),
        'CVSAddress' => $request->input('CVSAddress'),
        'CVSOutSide' => $request->input('CVSOutSide'),
        'ExtraData' => $request->input('ExtraData'),
    ];

    // 儲存到快取（以 MerchantTradeNo 為 key）
    $tradeNo = $request->input('MerchantTradeNo');
    cache()->put("store_selection_{$tradeNo}", $storeData, now()->addHours(1));

    // 回傳 HTML 頁面，通知父視窗關閉
    return response()->view('logistics.store-callback', ['store' => $storeData]);
});
```

### 方式一：彈出視窗（Popup）— 推薦

[](#方式一彈出視窗popup-推薦)

**優點**：不影響主頁面狀態，使用者體驗較佳

#### Vue 3 (Composition API)

[](#vue-3-composition-api)

```

      {{ loading ? '載入中...' : '選擇門市' }}

      門市名稱：{{ selectedStore.CVSStoreName }}
      門市代號：{{ selectedStore.CVSStoreID }}
      門市地址：{{ selectedStore.CVSAddress }}

import { ref, onMounted, onUnmounted } from 'vue'

const props = defineProps({
  subType: { type: String, default: 'UNIMARTC2C' },
  isCollection: { type: String, default: 'N' }
})

const emit = defineEmits(['store-selected'])

const loading = ref(false)
const selectedStore = ref(null)
let popup = null
let checkInterval = null

// 監聽來自回調頁面的訊息
const handleMessage = (event) => {
  // 驗證來源（請替換為你的網域）
  if (event.origin !== window.location.origin) return

  if (event.data?.type === 'STORE_SELECTED') {
    selectedStore.value = event.data.store
    emit('store-selected', event.data.store)

    if (popup && !popup.closed) {
      popup.close()
    }
  }
}

const openStoreMap = async () => {
  loading.value = true

  try {
    // 從後端取得表單資料
    const response = await fetch('/api/logistics/store-map', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.content
      },
      body: JSON.stringify({
        sub_type: props.subType,
        is_collection: props.isCollection
      })
    })

    const { action, fields } = await response.json()

    // 開啟彈出視窗
    const width = 800
    const height = 600
    const left = (screen.width - width) / 2
    const top = (screen.height - height) / 2

    popup = window.open('', 'ECPayStoreMap',
      `width=${width},height=${height},left=${left},top=${top},scrollbars=yes`)

    // 建立並提交表單到彈出視窗
    const form = document.createElement('form')
    form.method = 'POST'
    form.action = action
    form.target = 'ECPayStoreMap'

    Object.entries(fields).forEach(([name, value]) => {
      const input = document.createElement('input')
      input.type = 'hidden'
      input.name = name
      input.value = value
      form.appendChild(input)
    })

    document.body.appendChild(form)
    form.submit()
    document.body.removeChild(form)

    // 檢查彈出視窗是否關閉
    checkInterval = setInterval(() => {
      if (popup && popup.closed) {
        clearInterval(checkInterval)
        loading.value = false
      }
    }, 500)

  } catch (error) {
    console.error('開啟門市地圖失敗:', error)
    alert('開啟門市地圖失敗，請稍後再試')
  } finally {
    loading.value = false
  }
}

onMounted(() => {
  window.addEventListener('message', handleMessage)
})

onUnmounted(() => {
  window.removeEventListener('message', handleMessage)
  if (checkInterval) clearInterval(checkInterval)
})

```

#### React (Hooks)

[](#react-hooks)

```
import { useState, useEffect, useCallback } from 'react'

function StoreSelector({ subType = 'UNIMARTC2C', isCollection = 'N', onStoreSelected }) {
  const [loading, setLoading] = useState(false)
  const [selectedStore, setSelectedStore] = useState(null)

  // 監聽來自回調頁面的訊息
  useEffect(() => {
    const handleMessage = (event) => {
      if (event.origin !== window.location.origin) return

      if (event.data?.type === 'STORE_SELECTED') {
        setSelectedStore(event.data.store)
        onStoreSelected?.(event.data.store)
      }
    }

    window.addEventListener('message', handleMessage)
    return () => window.removeEventListener('message', handleMessage)
  }, [onStoreSelected])

  const openStoreMap = useCallback(async () => {
    setLoading(true)

    try {
      const response = await fetch('/api/logistics/store-map', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ sub_type: subType, is_collection: isCollection })
      })

      const { action, fields } = await response.json()

      // 開啟彈出視窗
      const width = 800, height = 600
      const left = (screen.width - width) / 2
      const top = (screen.height - height) / 2

      const popup = window.open('', 'ECPayStoreMap',
        `width=${width},height=${height},left=${left},top=${top},scrollbars=yes`)

      // 建立表單並提交
      const form = document.createElement('form')
      form.method = 'POST'
      form.action = action
      form.target = 'ECPayStoreMap'

      Object.entries(fields).forEach(([name, value]) => {
        const input = document.createElement('input')
        input.type = 'hidden'
        input.name = name
        input.value = value
        form.appendChild(input)
      })

      document.body.appendChild(form)
      form.submit()
      document.body.removeChild(form)

      // 監聯視窗關閉
      const checkClosed = setInterval(() => {
        if (popup?.closed) {
          clearInterval(checkClosed)
          setLoading(false)
        }
      }, 500)

    } catch (error) {
      console.error('開啟門市地圖失敗:', error)
      alert('開啟門市地圖失敗')
    } finally {
      setLoading(false)
    }
  }, [subType, isCollection])

  return (

        {loading ? '載入中...' : '選擇門市'}

      {selectedStore && (

          門市名稱：{selectedStore.CVSStoreName}
          門市代號：{selectedStore.CVSStoreID}
          門市地址：{selectedStore.CVSAddress}

      )}

  )
}

export default StoreSelector
```

### 回調頁面模板

[](#回調頁面模板)

建立 `resources/views/logistics/store-callback.blade.php`（Laravel Blade）：

```
>

    門市選擇完成

    門市選擇完成，視窗即將關閉...

        // 傳送訊息給父視窗
        if (window.opener) {
            window.opener.postMessage({
                type: 'STORE_SELECTED',
                store: @json($store)
            }, window.location.origin);

            // 延遲關閉視窗，確保訊息送達
            setTimeout(() => window.close(), 500);
        } else {
            // 如果是 iframe，傳送給父框架
            window.parent.postMessage({
                type: 'STORE_SELECTED',
                store: @json($store)
            }, window.location.origin);
        }

```

### 方式二：iframe 嵌入

[](#方式二iframe-嵌入)

適用於需要將門市地圖嵌入頁面內的場景：

```

import { ref, onMounted, onUnmounted } from 'vue'

const iframeSrc = ref('')
const mapFrame = ref(null)

const emit = defineEmits(['store-selected'])

// 使用隱藏表單提交到 iframe
const loadStoreMap = async () => {
  const response = await fetch('/api/logistics/store-map', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ sub_type: 'UNIMARTC2C' })
  })

  const { action, fields } = await response.json()

  // 建立隱藏 iframe 用於提交
  const iframe = document.createElement('iframe')
  iframe.name = 'storeMapFrame'
  iframe.style.width = '100%'
  iframe.style.height = '600px'
  iframe.style.border = 'none'

  const container = document.querySelector('.store-map-container')
  container.appendChild(iframe)

  // 建立表單提交到 iframe
  const form = document.createElement('form')
  form.method = 'POST'
  form.action = action
  form.target = 'storeMapFrame'

  Object.entries(fields).forEach(([name, value]) => {
    const input = document.createElement('input')
    input.type = 'hidden'
    input.name = name
    input.value = value
    form.appendChild(input)
  })

  document.body.appendChild(form)
  form.submit()
  document.body.removeChild(form)
}

const handleMessage = (event) => {
  if (event.data?.type === 'STORE_SELECTED') {
    emit('store-selected', event.data.store)
  }
}

onMounted(() => {
  window.addEventListener('message', handleMessage)
  loadStoreMap()
})

onUnmounted(() => {
  window.removeEventListener('message', handleMessage)
})

```

### 方式三：新分頁導向（傳統方式）

[](#方式三新分頁導向傳統方式)

適用於不需要保持 SPA 狀態的場景：

```
// 後端：儲存當前頁面 URL，供回調後重導
Route::post('/logistics/store-map-redirect', function (Request $request) {
    $factory = app('ecpay.logistics');

    // 儲存回調後要返回的頁面
    session(['return_url' => $request->input('return_url', '/')]);

    $storeMap = $factory->make('store_map')
        ->setMerchantTradeNo('ORDER_' . time())
        ->setLogisticsSubType(LogisticsSubType::UNIMART_C2C)
        ->setServerReplyURL(route('logistics.store-callback'));

    $formBuilder = new FormBuilder(config('ecpay-logistics.server'));

    // 直接輸出自動提交表單
    return response($formBuilder->autoSubmit($storeMap));
});

// 回調處理：重導回前端頁面
Route::post('/logistics/store-callback', function (Request $request) {
    $returnUrl = session('return_url', '/');

    // 將門市資訊附加到 URL query string
    $storeData = [
        'store_id' => $request->input('CVSStoreID'),
        'store_name' => $request->input('CVSStoreName'),
        'store_address' => $request->input('CVSAddress'),
    ];

    $redirectUrl = $returnUrl . '?' . http_build_query(['store' => $storeData]);

    return redirect($redirectUrl);
});
```

```
// React: 從 URL 讀取門市資訊
import { useSearchParams } from 'react-router-dom'

function CheckoutPage() {
  const [searchParams] = useSearchParams()
  const storeData = searchParams.get('store')

  const selectedStore = storeData ? JSON.parse(storeData) : null

  const openStoreMap = () => {
    // 建立表單導向到後端
    const form = document.createElement('form')
    form.method = 'POST'
    form.action = '/logistics/store-map-redirect'

    const input = document.createElement('input')
    input.type = 'hidden'
    input.name = 'return_url'
    input.value = window.location.pathname
    form.appendChild(input)

    // CSRF token
    const csrf = document.createElement('input')
    csrf.type = 'hidden'
    csrf.name = '_token'
    csrf.value = document.querySelector('meta[name="csrf-token"]')?.content
    form.appendChild(csrf)

    document.body.appendChild(form)
    form.submit()
  }

  return (

      選擇取貨門市
      {selectedStore && 已選擇：{selectedStore.store_name}}

  )
}
```

### Next.js (App Router) 整合

[](#nextjs-app-router-整合)

```
// app/api/logistics/store-map/route.ts
import { NextRequest, NextResponse } from 'next/server'

export async function POST(request: NextRequest) {
  const body = await request.json()

  // 呼叫 PHP 後端 API 或直接實作
  const response = await fetch(`${process.env.PHP_API_URL}/logistics/store-map`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body)
  })

  return NextResponse.json(await response.json())
}
```

```
// components/StoreSelector.tsx
'use client'

import { useState, useEffect } from 'react'

interface Store {
  CVSStoreID: string
  CVSStoreName: string
  CVSAddress: string
}

export default function StoreSelector({
  onSelect
}: {
  onSelect: (store: Store) => void
}) {
  const [loading, setLoading] = useState(false)

  useEffect(() => {
    const handleMessage = (event: MessageEvent) => {
      if (event.data?.type === 'STORE_SELECTED') {
        onSelect(event.data.store)
      }
    }

    window.addEventListener('message', handleMessage)
    return () => window.removeEventListener('message', handleMessage)
  }, [onSelect])

  const openStoreMap = async () => {
    setLoading(true)

    try {
      const res = await fetch('/api/logistics/store-map', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ sub_type: 'UNIMARTC2C' })
      })

      const { action, fields } = await res.json()

      const popup = window.open('', 'ECPayStoreMap',
        'width=800,height=600,scrollbars=yes')

      const form = document.createElement('form')
      form.method = 'POST'
      form.action = action
      form.target = 'ECPayStoreMap'

      Object.entries(fields as Record).forEach(([name, value]) => {
        const input = document.createElement('input')
        input.type = 'hidden'
        input.name = name
        input.value = value
        form.appendChild(input)
      })

      document.body.appendChild(form)
      form.submit()
      document.body.removeChild(form)

    } finally {
      setLoading(false)
    }
  }

  return (

      {loading ? '載入中...' : '選擇門市'}

  )
}
```

### 注意事項

[](#注意事項)

1. **跨域問題**：確保 `postMessage` 的 `origin` 驗證正確設定
2. **CSRF 保護**：API 請求需要包含 CSRF token
3. **彈出視窗阻擋**：部分瀏覽器可能阻擋彈出視窗，建議在使用者點擊事件中觸發
4. **行動裝置**：彈出視窗在行動裝置上體驗不佳，建議使用新分頁或 iframe 方式
5. **狀態保存**：SPA 在導向回來時可能丟失狀態，使用 `localStorage` 或後端 Session 保存

測試環境資訊
------

[](#測試環境資訊)

項目C2C 測試B2C 測試測試環境網址特店編號20001322000933HashKey5294y06JbISpM5x9XBERn1YOvpM9nfZcHashIVv77hoKGq4kWxNNISh1ONHk4P4yqbl5LK> 參考：[綠界物流整合 API 技術文件](https://developers.ecpay.com.tw/?p=7380)

相關資源
----

[](#相關資源)

- [綠界物流整合 API 技術文件](https://developers.ecpay.com.tw/?p=7380)
- [綠界特店管理後台（測試）](https://vendor-stage.ecpay.com.tw/)

授權
--

[](#授權)

MIT License

###  Health Score

35

—

LowBetter than 79% of packages

Maintenance76

Regular maintenance activity

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity49

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

163d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/6cc3f5bb28af8b207b65843e86f3b3e9feb1efd05b8ba888e1fa223a370ab822?d=identicon)[Carl Lee](/maintainers/Carl%20Lee)

---

Top Contributors

[![CarlLee1983](https://avatars.githubusercontent.com/u/8252510?v=4)](https://github.com/CarlLee1983 "CarlLee1983 (6 commits)")

---

Tags

cvsshippingpostTaiwanlogisticsecpayconvenience-store7-elevenfamily-marthilifetcatokmart

###  Code Quality

TestsPHPUnit

Code StylePHP\_CodeSniffer

### Embed Badge

![Health badge](/badges/carllee1983-ecpay-logistics/health.svg)

```
[![Health](https://phpackages.com/badges/carllee1983-ecpay-logistics/health.svg)](https://phpackages.com/packages/carllee1983-ecpay-logistics)
```

###  Alternatives

[grumpydictator/firefly-iii

Firefly III: a personal finances manager.

22.8k69.3k](/packages/grumpydictator-firefly-iii)[civicrm/civicrm-core

Open source constituent relationship management for non-profits, NGOs and advocacy organizations.

728272.9k17](/packages/civicrm-civicrm-core)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

255.2k](/packages/aedart-athenaeum)[ivanmitrikeski/laravel-shipping

Shipping package for Laravel. Supported providers: CanadaPost, USPS, UPS, FedEx and Purolator.

206.8k2](/packages/ivanmitrikeski-laravel-shipping)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
