Getting Started
Start by reading the package README and the v0.6.0 docs around the new single redirect checkout flow for mobile PG windows and the updated force-redirect flow. Those are the most relevant day-to-day changes in this release: mobile checkout should now be evaluated against the single-redirect path rather than assuming a popup/window-based flow.
Minimal setup path:
- Install the package with Composer and publish its config if the package exposes publishable config.
- Put PortOne credentials and environment settings in
.env, then confirm they are read through config/portone.php or the package’s documented config file.
- Register/confirm the return, callback, or webhook route that the PG will hit after payment.
- Build the smallest end-to-end flow: create an order in Laravel, initiate checkout, return from PG, verify the payment server-side, and mark the order paid.
First useful use case: a normal server-side checkout where Laravel creates a pending order and redirects the customer to PortOne. Treat the redirect back as untrusted input; the source of truth is the server-side PortOne verification call.
Implementation Patterns
Use the package as the transport/integration layer, but keep payment state and business rules in your application.
Server-initiated checkout
- Create an order/payment attempt record first, with a unique merchant identifier, amount, currency, and order name.
- Initiate the PortOne checkout from a controller or dedicated payment service.
- Redirect the customer to the PG checkout page.
- On return, look up the pending payment by merchant identifier, then verify status and amount through PortOne.
- Only transition the order to paid after verification succeeds.
Mobile PG windows
v0.6.0’s main feature is the single redirect checkout flow for mobile PG windows. For mobile checkout, prefer this flow when the package docs indicate it is supported for your PG channel. It avoids relying on popup/window behavior that mobile browsers and in-app webviews often block or lose.
A practical pattern is to keep one verification handler for both desktop and mobile:
// Conceptual flow
POST /checkout/{order} -> create pending payment, redirect to PG
GET /payments/return -> verify payment, update order, redirect to result page
POST /payments/webhook -> verify payment, update order idempotently
Force-redirect flow
The docs update for force-redirect flow is worth reading before changing existing behavior. If your app has a mix of desktop and mobile users, make the redirect strategy explicit per channel or per device rather than relying on implicit package behavior. Keep the initiation and return handling separate so you can switch between popup and redirect flows without rewriting order logic.
Payment verification
Create a small PaymentVerificationService or similar application service that:
- Loads the local payment attempt.
- Calls PortOne for the actual payment status.
- Compares the verified amount/currency with the local order.
- Handles already-paid, cancelled, failed, and duplicate-return cases.
- Dispatches domain events such as
PaymentSucceeded or PaymentFailed.
This keeps controllers thin and makes the same logic reusable from the redirect return route and any webhook/callback route.
Testing and integration
- Use a fake or sandbox gateway adapter in tests; do not let feature tests call PortOne.
- Test the return endpoint with forged query parameters and confirm it still rejects unverified payments.
- Test duplicate return/webhook calls and confirm the order is only marked paid once.
- Log payment initiation and verification request/response metadata, but mask credentials and sensitive customer data.
Gotchas and Tips
- Do not trust redirect parameters. A return URL containing
status=paid or a merchant identifier is not proof of payment. Always verify server-side with PortOne and compare the amount.
- Mobile sessions can be fragile. In-app browsers and external PG windows may lose session cookies. Store pending payment state in the database keyed by merchant identifier, not only in the session.
- Force-redirect behavior can surprise existing flows. After upgrading to v0.6.0, smoke-test desktop, mobile web, and in-app browser paths. The release notes are additive/docs-focused and do not call out breaking changes, but redirect behavior is exactly the kind of change that needs an end-to-end check.
- Return URLs must be production-safe. Use absolute HTTPS URLs and make sure they are whitelisted in the PortOne/PG console where required. Localhost URLs often work in sandbox but fail in production.
- Handle POST callbacks and CSRF deliberately. If the package or PG sends a POST callback/webhook, that route usually needs to be excluded from CSRF protection and protected by signature verification or server-side lookup instead.
- Make payment transitions idempotent. PG returns, browser refreshes, and webhook retries can all hit the same payment more than once. Use a unique merchant identifier and guard the paid transition.
- Clear config cache after environment changes. Laravel config caching is a common source of “wrong PortOne credentials” confusion.
- Keep the package behind an application-level payment service. That gives you a stable extension point if you later need to change PG channels, add refunds, or swap checkout flows without touching controllers.
- Review the v0.6.0 docs examples first. The single-redirect mobile flow is the new capability; the force-redirect documentation is the operational detail most likely to affect existing integrations.