hackware / hawese-payment
Multi-gateway payment processing for Hawese.
Requires
- php: >=7.1.3
- brick/money: ^0.6.0
- hackware/hawese-core: *
- kriswallsmith/buzz: ^1.0
- laravel/lumen-framework: 5.7.*|5.8.*
- nyholm/psr7: ^1.1
- ramsey/uuid: ^3.7
README
Overview
Pay through various payment gateways.
Main goals
- Web API with RESTful JSON responses.
- Pay through offsite payment gateways.
- Notify payment to custom HTTP service (such as the wallet API).
Features
- Multi-tenant, can be used with different configurations by
origin
. - As consumer I can perform a payment.
- As consumer I can
purchase
with the Khipu, Flow, DLocalGo and PayPal payment gateways. - As consumer I can
link
andcharge
payment methods through the Flow payment gateway. - As system I can receive payment notifications from offsite payment gateways so that a payment is confirmed.
- As system I can send a notification to a custom HTTP service so that a payment gets processed on that end.
Routes
GET /{origin}/gateways/
list of available gateways and schemasGET /{origin}/gateways/payment-methods/{schema=purchase}
list of all payment methods, includinggateway
andproperty
(which indicates the field that selects payment method remotely)GET /{origin}/gateways/{gateway}/schemas/{schema=purchase|link|charge}
fields required for schemaPOST /{origin}/gateways/{gateway}/{schema=purchase|link|charge}
do actionPOST /{origin}/gateways/{gateway}/notify
IPNPOST /{origin}/gateways/{gateway}/receipt/{paymentUuid}
emit receipt for existing paymentPOST /payments/{uuid}
payment status
Purchase flow
POST /{origin}/gateways/{gateway}/purchase
returns a JSON object with paymentuuid
andredirect_url
, which you need to redirect the user to, so they can pay.- Once user leaves the payment gateway,
user is redirected to origin's
return_url
with payment'suuid
query param. - In parallel,
a notification is issued to
POST /{origin}/gateways/{gateway}/notify
from the payment gateway.
Link flow
POST /{origin}/gateways/{gateway}/link
returns a JSON object withredirect_url
, which you need to redirect the user to, so they can link they payment method.- Once linked,
user is redirected to an endpoint in order to register linkage,
and then redirected to origin's
return_url
with the user'semail
query param.
Charge flow
POST /{origin}/gateways/{gateway}/charge
returns a JSON object with payment'suuid
.
Standard properties
Different providers will require you to send different parameters. In order to make its conversion a no-brainer for HTTP consumers, common fields have an standard name that will be translated to the proper property name for a specific provider, and set and as a readOnly default value when you request a schema and send these as query params.
- subject
- amount
- payment_method
- currency
A request example:
GET /gateways/khipu/schemas/purchase?email=hello@example.org&amount=10000
> ...
> "properties": {
> ...
> "amount": {
> "title": "Monto",
> "type": "number",
> "multipleOf": 1,
> "default": 10000,
> "readOnly": true
> },
> "payer_email": {
> "title": "Email",
> "type": "string",
> "format": "idn-email",
> "default": "hello@example.com",
> "readOnly": true
> },
> ...
>}
Additionally, you can always set a return_url
query param parameter when you
POST /gateways/{gateway}/purchase
in order to dynamically set the url to
return the user after he has paid or cancelled the payment (overriding
config('payment.return_url')
, a uuid
query param will be appended to this
url so you can verify its status. The reasoning for not including this field in
the schema is because it's never a user-set field.
Models
Gateways
Configuration based
- class: full class name (incluiding namespace) for this gateway.
- payment_methods: payment methods property name for each schema, used to list available payment methods for all available gateways.
- schemas: an associative array with valid JSON Schemas, each schema represents a form displayed to the final user containing data to send to the payment gateway (or exceptionally required by the processing payment method).
Payment
Database based
uuid, user_uid, gateway, currency, amount, description, detail, status
Origin
Configuration based, except gateway_credentials which is database based.
- return_url
- notify
- gateway_credentials: id, origin_id, gateway_id, test_mode, api_key, secret_key
- receipts
Run it
Requirements
Installation
Require the package on a hawese based project.
composer require hackware/hawese-payment
Run migrations.
php artisan migrate
Insert credentials for each origin and gateway. You need to specify if those correspond to test_mode
(development) or not (production).
php artisan payment:insert-credentials <origin> <gateway> <test_mode> <api_key> <secret_key>
If you need to request data to /payments/{uuid}
from origin, create a user with the origin as uuid. Then you can request such data using a token.
php artisan tinker
$user = new \Hawese\Core\User(['uid' => '<origin>']);
$user->insert();
$token = $user->generateSystemToken();
"{$token['key']}:{$token['secret']}" // copy output
Configure origins
This package will trigger a request to an external service automatically after reception of an Instant Payment Notification (IPN) from a payment gateway, take it as an external IPN which triggers an internal IPN (I think this is actually very handy :P).
It's configured to work with hackware/hawese-wallet by default, but it will work with any other endpoint if configured properly.
0. After a proper installation of this package, move to the base hawese
project.
- Append
$app->configure('payment');
underCustom configuration
onbootstrap/app.php
. - Copy the file
vendor/hackware/hawese-payment/config/payment.php
toconfig/payment.php
. - Append a proper config to the
origin
array, according to your origin's requirements.* - Setup setup gateway credentials for that origin with
php artisan payment:insert-credentials
(get help withphp artisan help payment:insert-credentials
).
* Proper config example:
'example_org' => [
'return_url' => 'https://example.org/checkout',
'notify' => [
'status' => ['completed', 'aborted'],
'uri' => 'https://example.org/verify-payment',
'headers' => [
'Content-Type' => 'application/json',
// or 'Content-Type' => 'application/x-www-form-urlencoded',
],
'method' => 'POST',
'params' => [
'status' => 'payment.status',
'uuid' => 'payment.uuid'
],
],
'receipts' => [
'libredteboleta' => [
'Encabezado' => [
'Emisor' => [
'RUTEmisor' => '11111111-1',
// 'CdgSIISucur' => '',
],
],
],
],
],
There's nothing cryptic about it. In the params
associative array setup your endpoint input fields as keys and payment attributes with the format payment.payment_model_attribute
.
Environment variables
DLOCALGO_COUNTRIES
enabled countries list, separated by spaces.PAYMENT_SOURCE_URL
is the place where to download the source code of your version of this project. Required.PAYMENT_WALLET_ENDPOINT
endpoint to a running hackware/hawese-wallet instance for IPN.PAYMENT_RETURN_URL
where to return the payer after a successful or failed payment, anuuid
query param will be appended to this URL, so you can call to thepayments.verify
route from that endpoint. hackware/userland is already prepared for that on the/add-funds/verify
endpoint.PAYMENT_CORE_AUTH_TOKEN
system token in key:secret format. It's returned by the hackware/hawesecomposer run-script seed
command, which runs the initial hackware/hawese-seeds.
Contributions
If you want to create a custom gateway please contact me first for guidelines, otherwise your contribution might be rejected (and implementation will be harder to grasp).
Error codes
UnexpectedResponseException
: Unexpected HTTP response.
UnexpectedValueException
: Unsupported request method.
LogicException
: Exclusive togetBodyOrQueryParams
, POST & GET parameters in the same request. Write this logic yourself.
Copyright and licensing
Copyright 2019-2023 Felix Freeman.
This project is licensed under the GNU Affero General Public License v3 or any later version.
How to comply with license terms?
To comply with license terms you must provide the means to access the source code of your version of the software easily and free of charge to any person that has access to the software, even users which access through a network (i.e. web browser). I've provided an easy way to accomplish this requirement by setting the PAYMENT_SOURCE_URL
environment variable, this will show the link on the index page (/) under the JSON links.payment_sourcecode
property.
Any modification or inclusion of this source code will inherit the same license, that is, it can't be sublicensed. But since it's a REST API you can consume it with any other software. That means that this software must always respect users freedom, even if the HTTP API is consumed by a privative one.
Other licensing options
If you want to use the software but are afraid of or can't comply with the license terms we can arrange other license terms for your case. Though most probably I'll not accept if you don't provide your modifications to the software back.