kpaph / msg4wrdio
SMS Gateway API
Requires
- php: ^8.1
- guzzlehttp/guzzle: ^7.2
- illuminate/support: ^8.0|^9.0|^10.0|^11.0|^12.0
This package is not auto-updated.
Last update: 2026-05-11 09:08:13 UTC
README
MSG4wrd is an SMS Gateway and Message Forwarder API formerly known as PTXT4wrd.
From 2005 to 2012, in the Philippines, some networks only allowed text messaging within the same network. Promos were available for unlimited texting to the same network, such as SMART to SMART or GLOBE to GLOBE only.
To address this issue, PTXT4wrd was invented, which allows users to send messages to other networks by forwarding their texts from their own network.
Example command for sending a message to other networks:
PTXT{space}OtherNetworkNumber{space}YourMessage then send to Gateway.
PTXT 09171234567 Hello world! Then, send to gateway number.
Gateways - SMART / GLOBE / SUN If you are smart, you will use SMART Gateway, identical to the other networks.
Requirements
- PHP
^8.1 - Laravel
8.x–12.x
Installation
composer require kpaph/msg4wrdio
The service provider is auto-discovered by Laravel — no manual registration in config/app.php is required.
Publish the config file (config/msg4wrdio.php):
php artisan vendor:publish --tag=msg4wrdio-config
Add your token to .env. Get a token at MSG4wrd.io:
MSG4wrdIO_TOKEN=YOUR-TOKEN-HERE
Demo routes (optional, disabled by default)
The package ships with demo routes (/msg4wrd, /msg4wrd/send, /msg4wrd/send-with-options) for smoke-testing. They are unauthenticated and can trigger SMS sends billed to your token, so they are disabled by default. To enable them in local environments only:
MSG4wrdIO_EXPOSE_DEMO_ROUTES=true
With the demo routes enabled you can check the install:
And trigger a test send:
Note: Only +63 (Philippines mobile) and +1 (US / Canada) numbers are accepted. A leading + and common separators (spaces, dashes, dots, parentheses) are stripped automatically. Examples of valid input: +63 917 123 4567, 639171234567, +1 (202) 664-5435, 12026645435.
Usage
Create a controller, e.g. SMSController:
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use KPAPH\MSG4wrdIO\Enums\SenderName; use KPAPH\MSG4wrdIO\Enums\Country; use KPAPH\MSG4wrdIO\Services\MSG4wrd; class SMSController extends Controller { // $options = [ // "sendername" => SenderName::Default | SenderName::MSG4wrd | "YourBrandID", // "priority" => 0 | 1, // "country" => Country::PH | Country::US, // ] // // sendername => Default = Typical Number / SIM-based / whatever is available // sendername => MSG4wrd = Uses the MSG4wrd sender name (charges more) // sendername => "BRANDID" = Your own brand ID, e.g. "GOOGLESMS", "YAHOOMSG" // // priority => 0 = Normal // priority => 1 = High (charges more) // // country => Country::PH = Philippines only // country => Country::US = US, Canada, and Philippines (charges more) public function SMSSendNormal() { return MSG4wrd::Send("US-PH-Number-Here", "Your-Message-Here"); } public function SMSSendWithOptions() { $options = [ "sendername" => SenderName::Default, "priority" => 0, "country" => Country::PH, ]; return MSG4wrd::Send("US-PH-Number-Here", "Your-Message-Here", $options); } }
Configuration reference
| Env variable | Default | Purpose |
|---|---|---|
MSG4wrdIO_TOKEN |
(empty) | Bearer token from MSG4wrd.io. |
MSG4wrdIO_DOMAIN |
https://api.msg4wrd.io |
Live (production) API base URL. |
MSG4wrdIO_DEVELOPER |
https://staging.msg4wrd.io |
Sandbox / staging API base URL, used when dev_mode=true. |
MSG4wrdIO_DEV_MODE |
false |
When true, requests go to the sandbox URL; when false, they go to live. |
MSG4wrdIO_DEFAULT_COUNTRY |
PH |
Default country if $options["country"] is omitted. |
MSG4wrdIO_EXPOSE_DEMO_ROUTES |
false |
Opt-in to register the unauthenticated /msg4wrd* demo routes. |
Changelog
Fixes
- Country option now actually works.
checkCountry()was comparing theCountryint-backed enum to the string"US", soCountry::USsilently fell through toCountry::PH. The helper now accepts aCountryenum, an int matching the enum value, or the string"US"/"PH". - Number validation. Renamed
chechNumberCountryCode→checkNumberCountryCodeand tightened the rules: only+63(PH mobile) and+1(US/CA) numbers are accepted. PH numbers must start with9after the country code; NANP numbers must have valid area + exchange codes (2-9). A leading+and common separators (space, dash, dot, parentheses) are stripped automatically. New helpersnormalizeNumber()anddetectCountry()are exposed alongsidecheckNumberCountryCode(). checkCountry()aliases. Accepts aCountryenum, the int value, or string aliases —PH/PHL/PHILIPPINESresolve toCountry::PH, andUS/USA/CA/CAN/CANADAresolve toCountry::US.- HTTP client. Removed dead Guzzle 3 exception imports, removed the brittle
(int)phpversion() < 8cURL fallback, fixed the stray space inAuthorization: ' Bearer ', added a 30s timeout, and any 2xx response is now treated as success. Non-2xx responses surface the upstream body and status instead of being collapsed to a genericSending failed.message. - Live / sandbox switch. The config now exposes separate
liveandsandboxURLs. SetMSG4wrdIO_DEV_MODE=trueto route requests to the sandbox (MSG4wrdIO_DEVELOPER); leave itfalsefor the live URL (MSG4wrdIO_DOMAIN). - Service provider. Moved
mergeConfigFrom()out ofboot()intoregister()where it belongs, removed the eagerapp->make(SampleController::class)call fromregister()(controllers should be resolved by the router), and the config publish is now gated behindrunningInConsole()and tagged asmsg4wrdio-config. Send()ergonomics.$optionsnow merges over defaults, so you can pass just the keys you care about.sendernameaccepts either aSenderNameenum or a custom brand-ID string.priorityis cast toint. A leading+is stripped from the mobile before sending.
Security
- Demo routes are off by default.
/msg4wrd,/msg4wrd/send, and/msg4wrd/send-with-optionswere previously auto-registered on every consuming app — anyone who could reach the app could trigger SMS sends billed to your token. They are now gated behindMSG4wrdIO_EXPOSE_DEMO_ROUTES=true. This is a breaking change if you relied on those routes — set the env var to re-enable them. - Removed the placeholder
[SECRET-TOKEN]default fromconfig/msg4wrdio.php; the token now defaults to an empty string. - Removed tracked
.DS_Storefiles and added a.gitignore.
Packaging
- Package auto-discovery. Added
extra.laravel.providerstocomposer.json. You no longer need to manually addMSG4wrdIOServiceProvider::classtoconfig/app.php. - Explicit constraints.
composer.jsonnow declaresphp: ^8.1andilluminate/support: ^8.0|^9.0|^10.0|^11.0|^12.0, so Composer can resolve the right Laravel version on install. - Switched
minimum-stabilitytostablewithprefer-stable: true.
Upgrading
If you are upgrading from an earlier version:
- Remove the manual
KPAPH\MSG4wrdIO\MSG4wrdIOServiceProvider::classentry fromconfig/app.php— auto-discovery handles it now. - If you relied on the
/msg4wrd*demo routes, addMSG4wrdIO_EXPOSE_DEMO_ROUTES=trueto your.env(recommended: local environments only). - Re-publish the config:
php artisan vendor:publish --tag=msg4wrdio-config --force. - If you called
MSG4wrd::chechNumberCountryCode(...)directly, rename it tocheckNumberCountryCode.