# Overview

Welcome to the Lahza Developer Documentation, where you can discover how to create exceptional payment experiences using the Lahza API

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Accept Payments</strong></td><td>Customers have the flexibility to make payments using any of the supported payment methods we offer.</td><td></td><td><a href="/payments/accept-payments">Accept Payments</a></td></tr><tr><td><strong>Recurring Charges</strong></td><td>Enable recurring charges with subscriptions or authorizations for seamless customer billing.</td><td></td><td><a href="/payments/recurring-charges">Recurring Charges</a></td></tr><tr><td><strong>Libraries and Plugins</strong></td><td>Integrate Lahza using your preferred language's plugins and libraries.</td><td></td><td><a href="/libraries-and-plugins/overview">Libraries and Plugins</a></td></tr></tbody></table>


# Accept Payments

{% hint style="info" %}
To facilitate payment acceptance, you can initiate a transaction through our API, our client JavaScript library, Popup JS, or our SDKs. Each transaction will generate a unique link that enables users to finalize the payment process seamlessly.
{% endhint %}

### Popup <a href="#popup" id="popup"></a>

Lahza Popup offers a user-friendly and hassle-free payment flow designed for web applications. Integrating it into your platform is a breeze and can be accomplished in just five simple steps, making it the most straightforward method to begin accepting payments.

#### Collect customer information <a href="#collect-customer-information" id="collect-customer-information"></a>

To initialize the transaction, you need to pass information such as email, mobile, amount, transaction reference, etc. The `key`, `email` ,`mobile` and `amount` parameters are the only required parameters. The table below lists the parameters that you can pass when initializing a transaction.

| Param     | Required? | Description                                                                                                                                                                                                                                                      |
| --------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| key       | Yes       | Your public key from Lahza. Use test key for test mode and live key for live mode                                                                                                                                                                                |
| email     | No        | Email address of customer                                                                                                                                                                                                                                        |
| mobile    | No        | Mobile number of customer                                                                                                                                                                                                                                        |
| firstName | No        | First name of the customer                                                                                                                                                                                                                                       |
| lastName  | No        | Last name of the customer                                                                                                                                                                                                                                        |
| amount    | Yes       | Amount (in the lowest currency value - agora, cents) you are debiting customer.                                                                                                                                                                                  |
| ref       | No        | Unique case sensitive transaction reference. Only -,., =and alphanumeric characters allowed. If you do not pass this parameter, Lahza will generate a unique reference for you.                                                                                  |
| currency  | No        | Currency charge should be performed in. Allowed values are: `ILS`, `JOD` or `USD` It defaults to your integration currency.                                                                                                                                      |
| channels  | No        | An array of payment channels to control what channels you want to make available to the user to make a payment with. Available channels include; `['card', 'bank', 'ussd', 'qr', 'mobile_money', 'bank_transfer']`                                               |
| metadata  | No        | Object containing any extra information you want recorded with the transaction. Fields within the `custom_field` object will show up on merchant receipt and within the transaction information on the Lahza Dashboard. You can learn more on the Metadata page. |
| label     | No        | String that replaces customer email as shown on the checkout form                                                                                                                                                                                                |
| onSuccess | No        | Function that runs when payment is successful. This should ideally be a script that uses the verify endpoint on the Lahza API to check the status of the transaction.                                                                                            |
| onCancel  | No        | Javascript function that is called if the customer closes the payment window instead of making a payment.                                                                                                                                                        |

{% tabs %}
{% tab title="HTML" %}

```html
<form id="form">
  <div class="form-group">
    <label for="email">Email Address</label>
    <input type="email" id="email" required />
  </div>
  <div class="form-group">
    <label for="amount">Amount</label>
    <input type="text" id="amount" required />
  </div>
  <div class="form-submit">
    <button type="submit" onclick="pay()"> Pay </button>
  </div>
</form>

<script src="https://js.lahza.io/inline.min.js"></script>
```

{% endtab %}

{% tab title="Javascript" %}

```javascript
<script>
    const paymentForm = document.getElementById('form');
    paymentForm.addEventListener("submit", pay, false);
    function pay(e) {
      e.preventDefault();
      const lahza = new LahzaPopup();
          lahza.newTransaction({
              key: 'pk_test_xxxxxxxx',
              email: document.getElementById("email").value,
              currency:"ILS",
              amount: document.getElementById("amount").value * 100,
              onSuccess: (transaction) => {
                   let message = 'Payment complete! Reference: ' + transaction.reference;
                   alert(message);
              },
              onCancel: () => {
                   alert('Window closed.');
              }
          });
    }
</script>
```

{% endtab %}
{% endtabs %}

In this sample, notice how:

1. The Lahza inline javascript is included using a `script` tag. This is how you import Lahza into your code.
2. The amount here can be hardcoded if you want to charge a specific amount.
3. The `Pay` button has been tied to an `onClick` function called `pay`. This is the action that causes the Lahza popup to load.

{% hint style="info" %}
If you don't collect customer email addresses, you can generate email addresses using the available information (e.g., phone numbers) along with your website URL.
{% endhint %}

#### Handle the callback method. <a href="#handle-the-callback-method" id="handle-the-callback-method"></a>

The callback method is fired when the transaction is successful. This is where you include any action you want to perform when the transaction is successful.

The recommended next step here is to [verify the transaction](/payments/verify-payments) to confirm the status.

{% hint style="info" %}
To verify a transaction, you need to set up a server route or page where you pass the transaction reference. From your server, you can call the Lahza verify endpoint to check the status of the transaction. The response from the verification is then returned to your frontend for further processing.
{% endhint %}

There are 2 ways you can call your server from the callback function

1. Make an AJAX request to the endpoint on your server that handles the transaction verification

<pre class="language-javascript"><code class="lang-javascript">onSuccess: function(response){
  $.ajax({
    url: 'http://example.com/verify?reference='+ response.reference,
    method: 'get',
    success: function (response) {
      // the transaction status is in response.data.status
<strong>    }
</strong>  });
}
</code></pre>

2. Redirect to the server URL by setting a `window.location` to the URL where the verification endpoint is set on your server.<br>

```javascript
onSuccess: function(response) {
  window.location = "http://example.com/verify.php?reference=" + response.reference;
};
// On the redirected page, you can call Lahza's verify endpoint.
```

{% hint style="warning" %}
For security reasons, it is essential to never directly call the Lahza API from your frontend. This prevents exposing your secret key on the client-side. Instead, all requests to the Lahza API should be initiated from your server. Your frontend can then receive the response from your server, ensuring the confidentiality of your secret key. This practice helps maintain the integrity of your payment system and safeguards sensitive information.
{% endhint %}

#### Verify the Transaction

After payment is made, the next step is to verify the transaction. Here's how to [verify transactions with Lahza](/payments/verify-payments).

#### Handle Webhook <a href="#handle-webhook" id="handle-webhook"></a>

When a payment is successful, Lahza sends a `charge.success` webhook event to your webhook URL. You can [learn more here](/payments/verify-payments).

### Redirect <a href="#redirect" id="redirect"></a>

Here, you call the Initialize TransactionAPI from your server to generate a checkout link, then redirect your users to the link so they can pay. After payment is made, the users are returned to your website at the `callback_url`<br>

{% hint style="warning" %}
Please ensure that your server is capable of establishing a TLSv1.2 connection with Lahza's servers. The majority of modern software supports this capability. If you encounter any SSL errors, it is recommended to contact your service provider for guidance and assistance in resolving the issue.
{% endhint %}

#### Initialize transaction <a href="#initialize-transaction-1" id="initialize-transaction-1"></a>

To initiate a transaction, follow these steps:

1. When a customer clicks the payment action button, send a POST request to our API to initialize the transaction. Include parameters such as email, amount, and any other required fields in the request to the Initialize Transaction API endpoint.
2. Upon a successful API call, we will provide you with an authorization URL. Redirect the customer to this URL to allow them to input their payment information and complete the transaction.

**Important notes:**

* Ensure that the amount field is converted to the lowest currency unit. Multiply the value by 100 to obtain the correct amount. For example, if you want to charge ULS50 or $50 or JOD50, multiply 50 by 100 and pass 5000 in the amount field.
* You should utilize a unique transaction identifier from your own system as the reference.
* If you wish to customize the callback URL for the transaction, set the callback\_url in the transaction\_data array. If not specified, we will use the callback URL set on your dashboard. Specifying the callback URL in the code allows you to be flexible with the redirect URL if necessary.
* If you do not set a callback URL on either the dashboard or in the code, users will not be redirected back to your site after completing the payment.
* For test transactions, you can set test callback URLs, while for live transactions, use live callback URLs. This distinction allows you to differentiate between test and live environments effectively.

<br>

```php
<?php
  $url = "https://api.lahza.io/transaction/initialize";

  $fields = [
    'email' => "customer@example.com",
    'mobile'=>"059912313"
    'amount' => "20000"
  ];

  $fields_string = http_build_query($fields);

  //open connection
  $ch = curl_init();
  
  //set the url, number of POST vars, POST data
  curl_setopt($ch,CURLOPT_URL, $url);
  curl_setopt($ch,CURLOPT_POST, true);
  curl_setopt($ch,CURLOPT_POSTFIELDS, $fields_string);
  curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    "Authorization: Bearer SECRET_KEY",
    "Cache-Control: no-cache",
  ));
  
  //So that curl_exec returns the contents of the cURL; rather than echoing it
  curl_setopt($ch,CURLOPT_RETURNTRANSFER, true); 
  
  //execute post
  $result = curl_exec($ch);
  echo $result;
?>
```

#### Verify Transaction <a href="#verify-transaction" id="verify-transaction"></a>

If the transaction is successful, Lahza will redirect the user back to a `callback_url` you set. We'll append the transaction reference in the URL. In the example above, the user will be redirected to `http://your_website.com/callback.php?reference=YOUR_REFERENCE`.

So you retrieve the reference from the URL parameter and use that to call the verify endpoint to confirm the status of the transaction. Learn more about [verifying transactions](/payments/webhooks).

It's very important that you call the Verify endpoint to confirm the status of the transactions before delivering value. Just because the `callback_url` was visited doesn't prove that transaction was successful.

#### Handle Webhook <a href="#handle-webhook-1" id="handle-webhook-1"></a>

When a payment is successful, Lahza sends a `charge.success` webhook event to webhook URL that you provide. Learn more about using [webhooks](/payments/verify-payments).

### Mobile SDKs <a href="#mobile-sdks" id="mobile-sdks"></a>

You can integrate Lahza directly into your Android or iOS app using our [mobile SDK](/libraries-and-plugins/libraries). For mobile frameworks like Ionic or React Native, please [find the libraries here](/libraries-and-plugins/libraries).


# Webhooks

{% hint style="info" %}
Webhooks provide a convenient way to establish a notification system for receiving updates on specific requests made to the Lahza API.
{% endhint %}

### Introduction <a href="#introduction" id="introduction"></a>

Typically, when making a request to an API endpoint, a prompt response is anticipated. However, certain requests may require a longer processing time, potentially resulting in timeout errors. To mitigate such errors, an interim response known as a pending response is returned. To obtain the final status of the request and update your records accordingly, you have two options:

1. Polling: You can periodically make requests to check for updates on the request by polling the API endpoint. or,
2. Webhooks: Alternatively, you can set up a webhook URL to listen for events. This way, you will receive automatic notifications whenever there is an update on the request, eliminating the need for continuous polling.

{% hint style="info" %}
Choose webhooks over callbacks or polling for better control and reliability. Callbacks can fail due to network issues or device shutdown, while webhooks provide more consistent and efficient notifications.
{% endhint %}

### Polling vs Webhooks <a href="#polling-vs-webhooks" id="polling-vs-webhooks"></a>

[<br>](https://paystack.com/docs/static/4e48ac469a56d3f950460a6f7203b0fe/4352a/polling_webhooks.png)To obtain the final status of a request through polling, you need to regularly send GET requests at specific intervals. For instance, when a customer makes a payment for a transaction, you would continuously request the transaction status until it becomes successful.

On the other hand, webhooks enable the resource server (such as Lahza) to send updates to your server whenever there is a change in the request status. These status changes are referred to as events, which you can conveniently listen to on a designated POST endpoint known as your webhook URL.

The table below summarizes the disparities between polling and webhooks:<br>

|             | Polling                                                    | Webhooks                                                          |
| ----------- | ---------------------------------------------------------- | ----------------------------------------------------------------- |
| Mechanism   | Periodically sending GET requests for status updates       | Receiving automatic updates from the resource server              |
| Workflow    | Continuously checking for updates                          | Listening for events through a webhook URL                        |
| Efficiency  | Requires frequent requests, resulting in higher API usage  | Reduces API usage as updates are pushed from the resource server  |
| Real-time   | Updates may not be real-time due to polling intervals      | Real-time updates as they are pushed from the resource server     |
| Reliability | Relies on successful API requests and network connectivity | More reliable as updates are directly sent by the resource server |

### Create a webhook URL <a href="#create-a-webhook-url" id="create-a-webhook-url"></a>

\
A webhook URL is essentially a POST endpoint where a resource server sends updates. This URL should be capable of parsing a JSON request and responding with a 200 OK status code.

{% tabs %}
{% tab title="Node.JS" %}
{% code lineNumbers="true" %}

```javascript
const express = require('express');
const app = express();

app.use(express.json());

app.post('/webhook', (req, res) => {
  const data = req.body;
  // Process the data or perform desired actions
  // ...
  res.sendStatus(200);
});

app.listen(3000, () => {
  console.log('Webhook server is running on port 3000');
});
```

{% endcode %}
{% endtab %}

{% tab title="PHP" %}
{% code lineNumbers="true" %}

```php
$data = json_decode(file_get_contents('php://input'), true);
// Process the data or perform desired actions
// ...

http_response_code(200);
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
from flask import Flask, request

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def handle_webhook():
    data = request.get_json()
    # Process the data or perform desired actions
    # ...
    return '200 OK'

if __name__ == '__main__':
    app.run()
```

{% endcode %}
{% endtab %}
{% endtabs %}

Once your webhook URL receives an event, it must parse and acknowledge the event by returning a 200 OK status in the HTTP header. If the response header does not contain a 200 OK status, we will continue sending events for the next 72 hours.

* In live mode, webhooks are initially sent every 3 minutes for the first 4 attempts. After that, the frequency switches to hourly for the next 72 hours.
* In test mode, webhooks are sent hourly for the next 72 hours.

{% hint style="warning" %}
To ensure proper handling of long-running tasks within your webhook function, it is essential to acknowledge the event before executing those tasks. Failure to do so may result in a request timeout and an automatic error response from your server. Remember that a 200 OK response is crucial to avoid retries, as described in the previous paragraph.
{% endhint %}

### Verify event origin <a href="#verify-event-origin" id="verify-event-origin"></a>

\
To maintain the security and integrity of your webhook URL, it is crucial to verify that the events originate from Lahza rather than from malicious actors. You can employ two methods to ensure the authenticity of events sent to your webhook URL:

1. Signature Validation
2. IP Whitelisting

#### Signature validation <a href="#signature-validation" id="signature-validation"></a>

Lahza sends events with the `x-lahza-signature` header, which contains a HMAC SHA256 signature of the event payload. Before processing the event, it is important to verify this header signature to ensure the integrity of the payload.

{% tabs %}
{% tab title="NodeJS" %}

```javascript
const crypto = require('crypto');
const secret = process.env.SECRET_KEY;

app.post("/my/webhook/url", (req, res) => {
  // Validate event
  const hash = crypto.createHmac('sha256', secret).update(req.body).digest('hex');
  if (hash === req.headers['x-lahza-signature']) {
    // Retrieve the request's body
    const event = req.body;
    // Do something with the event
    // ...
  }
  res.sendStatus(200);
});
```

{% endtab %}

{% tab title="PHP" %}
{% code lineNumbers="true" %}

```php
$secretKey = 'YOUR_SECRET_KEY';

// Retrieve the payload and signature from the request
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_LAHZA_SIGNATURE'];

// Verify signature
$hash = hash_hmac('sha256', $payload, $secretKey);
if ($hash === $signature) {
    // Signature is valid, process the event
    $event = json_decode($payload, true);
    // Do something with the event
    // ...
}

http_response_code(200);

```

{% endcode %}
{% endtab %}

{% tab title="C#" %}
{% code lineNumbers="true" %}

```csharp
using System;
using System.Security.Cryptography;
using System.Text;

public class WebhookVerification
{
    private const string SecretKey = "YOUR_SECRET_KEY";

    public static void Main(string[] args)
    {
        string payload = // Retrieve the payload from the request
        string signature = // Retrieve the signature from the request headers

        // Verify signature
        string hmacDigest = CalculateHmac(payload, SecretKey, "HMACSHA256");
        if (hmacDigest.Equals(signature))
        {
            // Signature is valid, process the event
            // Do something with the event
            // ...
        }
    }

    private static string CalculateHmac(string payload, string secretKey, string algorithm)
    {
        byte[] keyBytes = Encoding.UTF8.GetBytes(secretKey);
        byte[] payloadBytes = Encoding.UTF8.GetBytes(payload);

        using (HMACSHA512 hmac = new HMACSHA512(keyBytes))
        {
            byte[] hmacBytes = hmac.ComputeHash(payloadBytes);
            return BitConverter.ToString(hmacBytes).Replace("-", string.Empty).ToLower();
        }
    }
}

```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code lineNumbers="true" %}

```java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.util.Base64;
import java.nio.charset.StandardCharsets;

public class WebhookVerification {
    private static final String SECRET_KEY = "YOUR_SECRET_KEY";

    public static void main(String[] args) {
        String payload = // Retrieve the payload from the request
        String signature = // Retrieve the signature from the request headers

        // Verify signature
        String hmacDigest = calculateHmac(payload, SECRET_KEY, "HmacSHA256");
        if (hmacDigest.equals(signature)) {
            // Signature is valid, process the event
            // Do something with the event
            // ...
        }
    }

    private static String calculateHmac(String payload, String secretKey, String algorithm) {
        try {
            Mac mac = Mac.getInstance(algorithm);
            SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), algorithm);
            mac.init(keySpec);
            byte[] hmacBytes = mac.doFinal(payload.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(hmacBytes);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code lineNumbers="true" %}

```python
import hashlib
import hmac
from flask import Flask, request

app = Flask(__name__)
secret_key = 'YOUR_SECRET_KEY'

@app.route('/my/webhook/url', methods=['POST'])
def handle_webhook():
    payload = request.get_data()
    signature = request.headers.get('x-lahza-signature')

    # Verify signature
    hmac_digest = hmac.new(secret_key.encode(), payload, hashlib.sha256).hexdigest()
    if hmac.compare_digest(hmac_digest, signature):
        # Signature is valid, process the event
        event = request.json
        # Do something with the event
        # ...

    return 'OK'

if __name__ == '__main__':
    app.run()
```

{% endcode %}
{% endtab %}
{% endtabs %}

#### IP whitelisting <a href="#ip-whitelisting" id="ip-whitelisting"></a>

\
To restrict access to your webhook URL and only allow requests from specific IP addresses, it is recommended to whitelist the following IP addresses for Lahza:

* `161.35.20.140`
* `209.38.219.189`

By whitelisting these IP addresses and blocking requests from other IP addresses, you can ensure that only legitimate requests from Lahza are accepted, while considering requests from other IP addresses as potentially unauthorized.

{% hint style="info" %}
**Whitelisting is Domain Independent:**

It's important to note that the IP addresses mentioned above are applicable to both the test and live environments. You can whitelist these IP addresses in both your staging and production environments, ensuring consistent and secure webhook handling across different stages of your application.
{% endhint %}

### Go live checklist <a href="#go-live-checklist" id="go-live-checklist"></a>

\
To ensure a smooth experience with your webhook URL, consider the following suggestions:

1. Add the webhook URL on your Lahza dashboard: Make sure to register and configure your webhook URL in your Lahza dashboard. This allows Lahza to send event updates to your specified URL.
2. Ensure your webhook URL is publicly available: It is important that your webhook URL is accessible from the internet. Localhost URLs cannot receive webhook events. Ensure that your webhook URL is publicly accessible for successful event delivery.
3. Trailing slash in .htaccess (if applicable): If you are using .htaccess to handle URL rewriting, remember to include a trailing slash (/) at the end of the URL to ensure proper routing.
4. Test your webhook: Before deploying your webhook in a production environment, test it to ensure that you receive the JSON body of the events and respond with a 200 OK HTTP status code. This confirms that your webhook is functioning correctly.
5. Handle long-running tasks: If your webhook function involves long-running tasks, it is recommended to acknowledge the webhook event by returning a 200 OK response before proceeding with those tasks. This prevents request timeouts and allows for efficient processing of events.
6. Failure handling: If Lahza does not receive a 200 OK HTTP response from your webhook, it will be considered a failed attempt. In live mode, failed attempts are retried every 3 minutes for the first 4 tries. After that, the retry interval switches to hourly for the next 72 hours. Similarly, in test mode, failed attempts are retried hourly for the next 72 hours.

By following these guidelines, you can ensure a seamless and reliable webhook integration with Lahza.

### Supported events <a href="#supported-events" id="supported-events"></a>

{% tabs %}
{% tab title="First Tab" %}

{% endtab %}

{% tab title="Second Tab" %}

{% endtab %}
{% endtabs %}

### Types of events <a href="#types-of-events" id="types-of-events"></a>

| Event               | Description                                                                  |
| ------------------- | ---------------------------------------------------------------------------- |
| `charge.success`    | A successful charge was made                                                 |
| `refund.failed`     | Refund cannot be processed. Your account will be credited with refund amount |
| `refund.pending`    | Refund initiated, waiting for response from the processor.                   |
| `refund.processed`  | Refund has successfully been processed by the processor.                     |
| `refund.processing` | Refund has been received by the processor.                                   |

<br>

<br>

<br>


# Verify Payments

## Verify Payments

Verify transactions after payments using Lahza's verify API

### How to verify payments <a href="#how-to-verify-payments" id="how-to-verify-payments"></a>

You do this by making a `GET` request to the Verify [TransactionAPI endpoint](https://api-docs.lahza.io/api-endpoints/transactions#verify-transaction) from your server using your transaction reference. This is dependent on the method you used to initialize the transaction.

#### From Popup or Mobile SDKs <a href="#from-popup-or-mobile-sdks" id="from-popup-or-mobile-sdks"></a>

You'll have to send the reference to your server, then from your server you call the verify endpoint.

#### From the Redirect API <a href="#from-the-redirect-api" id="from-the-redirect-api"></a>

You initiate this request from your callback URL. The transaction reference is returned as a query parameter to your callback URL.

{% hint style="info" %}
Always confirm that you have not already delivered value for that transaction to avoid double fulfillments, especially, if you also use webhooks.
{% endhint %}

Here's a code sample for verifying transactions:

{% tabs %}
{% tab title="Request" %}

```url
curl https://api.lahza.io/transaction/verify/:reference
-H "Authorization: Bearer YOUR_SECRET_KEY"
-X GET
```

{% endtab %}

{% tab title="Response" %}

```json
{
  "status": true,
  "message": "Verification successful",
  "data": {
    "id": 690075529,
    "domain": "test",
    "status": "success",
    "reference": "XXXXX",
    "amount": 10000,
    "message": "Approval",
    "gateway_response": "Successful",
    "paid_at": "2023-01-1T12:30:56.000Z",
    "created_at": "2023-01-1T12:26:44.000Z",
    "channel": "card",
    "currency": "ILS",
    "ip_address": "XXXX",
    "metadata": "",
    "log": {
      "start_time": 1589891451,
      "time_spent": 6,
      "attempts": 1,
      "errors": 0,
      "success": true,
      "mobile": false,
      "input": [],
      "history": [
        {
          "type": "action",
          "message": "Attempted to pay with card",
          "time": 5
        },
        {
          "type": "success",
          "message": "Successfully paid with card",
          "time": 6
        }
      ]
    },
    "fees": 100,
    "authorization": {
      "authorization_code": "AUTH_xxxxxxxxxx",
      "bin": "408112",
      "last4": "1000",
      "exp_month": "12",
      "exp_year": "2028",
      "channel": "card",
      "card_type": "visa DEBIT",
      "bank": "Test Bank",
      "country_code": "PS",
      "brand": "visa",
      "reusable": true,
      "signature": "SIG_xxxxxxxxxxxxxxx",
      "account_name": null
    },
    "customer": {
      "id": 24259516,
      "first_name": null,
      "last_name": null,
      "email": "customer@email.com",
      "customer_code": "CUS_xxxxxxxxxxx",
      "phone": null,
      "metadata": null,
      "risk_action": "default"
    }
  }
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
The API response includes a key called `response.status`, which indicates the status of the API call itself. It is important to note that this is not the status of the transaction. To obtain the status of the transaction, you need to refer to the `data` object within the verify API response, specifically `response.data.status`. For more detailed information about the format of the Lahza API responses, please refer to the documentation to gain a comprehensive understanding.
{% endhint %}

### Charge returning Users <a href="#charge-returning-users" id="charge-returning-users"></a>

The verify response also provides information about the payment instrument used by the user in the `data.authorization` object. If the channel indicates a card payment, you can store the `authorization_code` associated with that card for the respective user. This stored authorization code can then be used for future transactions to charge the user conveniently without requiring them to re-enter their payment details. This allows for a seamless payment experience for subsequent transactions. Learn more about [recurring charges](/payments/recurring-charges).


# Recurring Charges

{% hint style="info" %}
**In summary**\
once a customer has successfully made their initial payment using a card, you have the option to store their card authorization and utilize it for future transactions. It's important to note that this functionality is currently only applicable for card payments.
{% endhint %}

### Charge the first transaction <a href="#charge-the-first-transaction" id="charge-the-first-transaction"></a>

You can initiate the first charge either from a web application or a mobile app. Explore the various [integration methods available for both web and mobile platforms](/libraries-and-plugins/overview) to effectively implement this functionality.

#### Why is charging the user required when adding their cards?

1. Local regulations mandate that users must undergo a two-factor authentication (2FA) process during the initial transaction to authenticate their card. Only after successful authentication can we proceed with charging the card for subsequent transactions.
2. This requirement enables us to verify the validity of the card and ensures that it can be used for subsequent transactions without any issues.

{% hint style="warning" %}
**Minimum charge amount**

We suggest a minimum amount of ILS 0.5, JOD 0.1, or USD 0.20 for the initial charge. Please note that lower amounts may not be guaranteed to work with all card brands or banks.

As part of our standard practice, we credit the user's app with a value equivalent to the tokenization amount, or alternatively, we issue a refund for the charged amount. This ensures that the user's funds are not permanently deducted and provides a seamless experience for them.
{% endhint %}

### Get the Card authorization <a href="#get-the-card-authorization" id="get-the-card-authorization"></a>

Upon a successful first transaction, you have two options for obtaining transaction updates. Firstly, you can set up a [webhook](/payments/webhooks) endpoint to receive events in real-time. Alternatively, you can utilize the [Verify Transaction API endpoint](https://api-docs.lahza.io/api-endpoints/transactions#verify-transaction) to check the status of the transaction. In both cases, the response will follow the sample format provided below:

```json
{  
  ...
  "data": {  
    ...
    "authorization": {  
      "authorization_code":"AUTH_XXXXXX",
      "card_type":"visa",
      "last4":"0444",
      "exp_month":"01",
      "exp_year":"2023",
      "bin":"401234",
      "bank":"TEST BANK",
      "channel":"card",
      "signature": "SIG_XXXXXX",
      "reusable":true,
      "country_code":"PS",
    },
    ...
  }  
}
```

In the response, you will observe that the data object includes an authorization object. This authorization object provides detailed information about the payment instrument used by the user, specifically the card details.

### Store the authorization <a href="#store-the-authorization" id="store-the-authorization"></a>

\
Afterwards, you should save the authorization information and the email used for the transaction. These details will be useful for charging the card in future transactions. It's important to note that each payment method used on your website or app has a unique identifier. This identifier helps prevent the storage of duplicate authorizations, so you won't save the same information multiple times.

{% hint style="info" %}
To maintain complete card-related information, it is crucial to store the entire authorization object. This ensures that no context is lost regarding the card details.

Additionally, it is essential to store the email address used during the authorization creation process. Only the email associated with the initial authorization can be used for subsequent charges. If you rely on the user's email stored in your system and the user modifies it, the authorization will no longer be chargeable. Therefore, storing the original email used for authorization is necessary to ensure the successful processing of future charges.
{% endhint %}

By saving the complete authorization object, you gain the ability to display the customer's payment details during subsequent payment attempts. This allows for a seamless recurrent charging process. For instance, when the user intends to make another payment, you can conveniently present the card details as "Bank of Palestine Visa card ending with 1234" to the user. This facilitates a smooth and straightforward payment experience.

### Charge the authorization <a href="#charge-the-authorization" id="charge-the-authorization"></a>

\
When the user chooses a specific card for a new transaction or when you need to charge them in subsequent transactions, you will need to send the authorization code, the user's email, and the desired amount to the [Charge Authorization API](https://api-docs.lahza.io/api-endpoints/transactions#charge-authorization). This API enables you to initiate the charge process by providing the necessary information to authorize the transaction and complete the payment.

{% tabs %}
{% tab title="Request" %}
{% code lineNumbers="true" %}

```
curl https://api.lahza.io/transaction/charge_authorization
-H "Authorization: Bearer YOUR_SECRET_KEY"
-H "Content-Type: application/json"
-d '{ "authorization_code" : "AUTH_XXXXXXX", email: "example@example.com", amount: "10000" }'
-X POST
```

{% endcode %}
{% endtab %}

{% tab title="Response" %}
{% code overflow="wrap" lineNumbers="true" %}

```json
{
  "status": true,
  "message": "Charge attempted",
  "data": {
    "amount": 10000,
    "currency": "ILS",
    "transaction_date": "2023-06-01T10:12:13.000Z",
    "status": "success",
    "reference": "asjdasd823jds",
    "domain": "test",
    "metadata": "",
    "gateway_response": "Approved",
    "message": null,
    "channel": "card",
    "ip_address": null,
    "log": null,
    "fees": 14500,
    "authorization": {
      "authorization_code": "AUTH_asokdjsakdja",
      "bin": "13467",
      "last4": "4011",
      "exp_month": "11",
      "exp_year": "2023",
      "channel": "card",
      "card_type": "visa DEBIT",
      "bank": "Test Bank",
      "country_code": "PS",
      "brand": "visa",
      "reusable": true,
      "signature": "SIG_oajdskaJjsd82",
    },
    "customer": {
      "id": 123,
      "first_name": null,
      "last_name": null,
      "email": "example@example.com",
      "customer_code": "CUS_klmasdmka82",
      "phone": null,
      "metadata": null,
      "risk_action": "default"
    },
    "plan": null,
    "id": 12312456
  }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Interval-based Charging**\
If your application requires charging authorizations at specific intervals, you will need to set up a cron job on your server. This cron job will run at designated intervals and retrieve all the authorizations that need to be charged.
{% endhint %}

<br>

\ <br>

<br>

<br>

<br>


# Manage Disputes


# Refunds


# Test Payments

Test the various payment channels using the following test details.

#### Successful Cards <a href="#successful-cards" id="successful-cards"></a>

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th><th></th></tr></thead><tbody><tr><td>Card Type: Visa</td><td>Card Number: 4111111111111111</td><td>CVV: 004</td><td>Exp: 03/30</td></tr><tr><td>Card Type: MasterCard</td><td>Card Number: 5424000000000015</td><td>CVV: 004</td><td>Exp: 03/30</td></tr></tbody></table>

#### Failed Cards

{% hint style="info" %}
Enter different CVV to have Invalid CVV\
Enter any different date to have Invalid expiry date
{% endhint %}

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th><th></th><th></th></tr></thead><tbody><tr><td>Insufficient fund</td><td>Card Type: Visa</td><td>Card Number: 4000000000009995</td><td>CVV: 004</td><td>Exp: 03/30</td></tr><tr><td>Do Not Honour</td><td>Card Type: Visa</td><td>Card Number: 4000000000009979</td><td>CVV: 004</td><td>Exp: 03/30</td></tr><tr><td>Authentication failed</td><td>Card Type: Visa</td><td>Card Number: 4000000000000002</td><td>CVV: 004</td><td>Exp: 03/30</td></tr><tr><td></td><td></td><td></td><td></td><td></td></tr></tbody></table>


# Metadata

Enhance your request payload with custom data.

### Designing Metadata

With metadata, you can include extra parameters that may not be naturally supported by an endpoint. The process of creating metadata depends on how your programming language handles JSON. Some commonly used metadata includes:

* Invoice ID
* Cart ID
* Cart Items

There are two methods to include parameters in the metadata object:

1. **Key/value pair**: To include a parameter using this method, you pass it as a key/value pair. For example, "cart\_id: 928324". Parameters passed in this way may not be visible on the dashboard, but they are included in the API response.
2. **Custom Fields:** The "custom\_fields" key is specifically designated for an array of custom fields that will be displayed on the dashboard when you click on the transaction.

\
Custom fields consist of three keys: "display\_name", "variable\_name", and "value". The "display\_name" represents the label or name associated with the displayed value.

{% code overflow="wrap" lineNumbers="true" %}

```json
"metadata":{
  "cart_id":1,
  "custom_fields":[
    {
      "display_name":"Invoice ID",
      "variable_name":"Invoice ID",
      "value":122
    },
    {
      "display_name":"Cart Items",
      "variable_name":"cart_items",
      "value":"Coffee"
    }
  ]
}
```

{% endcode %}

### Cancel Action <a href="#cancel-action" id="cancel-action"></a>

To redirect users to a specific URL when they cancel a payment, you can utilize the "cancel\_action" attribute within your metadata.

{% code overflow="wrap" lineNumbers="true" %}

```json
"metadata": {
  "cancel_action": "https://example.com"
}
```

{% endcode %}

<br>

<br>

<br>


# Overview

## Libraries and Plugins

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><strong>Choose a Library</strong></td><td><p></p><ul><li> PHP</li><li> Java</li><li> NodeJS</li><li> Python</li><li><a href="/libraries-and-plugins/libraries"> View all</a></li></ul></td></tr><tr><td><strong>Choose a Plugin</strong></td><td><p></p><ul><li> WordPress</li><li> Shopify</li><li> Magento</li><li> OpenCart</li><li> <a href="/libraries-and-plugins/plugins">View all</a></li></ul></td></tr></tbody></table>


# Libraries


# Plugins

### WordPress <a href="#wordpress" id="wordpress"></a>

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Lahza WooCommerce</strong></td><td><a href="https://test.com">https://test.com</a></td></tr></tbody></table>

### Shopify <a href="#shopify" id="shopify"></a>

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Shopify</strong></td><td><a href="https://shopify.lahza.io">https://shopify.lahza.io</a></td></tr></tbody></table>

### Magento <a href="#magento" id="magento"></a>

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Magento 1.9</strong></td><td><a href="broken://spaces/sDzHN5xnoZNirkNIxrdn">Broken link</a></td></tr><tr><td><strong>Magento 2.x</strong></td><td><a href="broken://spaces/sDzHN5xnoZNirkNIxrdn">Broken link</a></td></tr></tbody></table>

**OpenCart**

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>OpenCart</strong></td><td><a href="broken://spaces/sDzHN5xnoZNirkNIxrdn">Broken link</a></td></tr></tbody></table>

### Prestashop <a href="#prestashop" id="prestashop"></a>

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>PrestaShop 1.5 - 1.6</strong></td><td><a href="broken://spaces/sDzHN5xnoZNirkNIxrdn">Broken link</a></td></tr><tr><td><strong>PrestaShop 1.7</strong></td><td><a href="broken://spaces/sDzHN5xnoZNirkNIxrdn">Broken link</a></td></tr></tbody></table>


# Overview

Discover how to seamlessly incorporate Lahza into your product.

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Accept Payments on your Vue App</td><td>Effortlessly integrate Lahza into your Vue.js application.</td><td></td></tr><tr><td>Accept Payments on your React App</td><td>Effortlessly integrate Lahza into your React application.</td><td></td></tr><tr><td>Accept Payments on your Android App</td><td>Effortlessly integrate Lahza into your Android application.</td><td></td></tr><tr><td>Accept Payments on your iOS App</td><td>Effortlessly integrate Lahza into your iOS application.</td><td></td></tr><tr><td>Using the Lahza Checkout in a Mobile WebView</td><td>Effortlessly integrate Lahza into your mobile applications.</td><td><a href="/guide/checkout-in-a-mobile-webview">Checkout in a Mobile WebView</a></td></tr></tbody></table>


# Checkout in a Mobile WebView

### Why use a WebView? <a href="#why-use-a-webview" id="why-use-a-webview"></a>

There are several compelling reasons to consider utilizing the hosted checkout in a WebView rather than integrating directly with the mobile SDK. It is important to note that regardless of the chosen method, the payments are still processed by the same underlying APIs. The distinction lies in the payment experience that you provide to your customers. Here are some advantages of using a WebView:

1. By utilizing our hosted checkout, Lahza takes complete control of the payment experience. This relieves you from the burden of building a checkout UI within your mobile application, significantly reducing the amount of code required for seamless payment integration.
2. While the mobile SDK only supports card payments, the checkout offers a broader range of non-card payment options. This includes USSD, QR, EasyLife, and Pay-with-Bank, providing a wider array of payment choices for your customers.

### Generating the checkout URL <a href="#generating-the-checkout-url" id="generating-the-checkout-url"></a>

To begin the integration process, the first step is to initialize the transaction, which will generate a checkout URL for further processing.

\
When a customer clicks the payment action button, you can initialize a transaction by sending a POST request from your server to our API. Include the required parameters such as email, amount, and any other relevant optional parameters when calling the Initialize [Transaction API endpoint.](https://api-docs.lahza.io/api-endpoints/transactions#create-transaction)

{% tabs %}
{% tab title="Request" %}

```
curl https://api.lahza.io/transaction/initialize
-H "Authorization: Bearer YOUR_SECRET_KEY"
-H "Content-Type: application/json"
-d '{ "email": "example@example.com", "amount": "10000" }'
-X POST
```

{% endtab %}

{% tab title="Response" %}

```json
{
  "status": true,
  "message": "Authorization URL created",
  "data": {
    "authorization_url": "https://checkout.lahza.io/keigjNjru82",
    "access_code": "jsj823JUD",
    "reference": "dj28ejU"
  }
}
```

{% endtab %}
{% endtabs %}

### Displaying Checkout in Your WebView <a href="#displaying-checkout-in-your-webview" id="displaying-checkout-in-your-webview"></a>

Once the API call is successful, Lahza will provide an authorization URL in the response. You should then retrieve this URL and pass it back to your frontend application. Load the obtained URL in the WebView widget to display the payment interface. Refer to the sample code below for an example implementation:

{% hint style="info" %}
**Allow Redirect**

Enable redirects in the WebView widget to ensure smooth bank and 3DS authorization. Blocking redirects may prevent customers from completing transactions.
{% endhint %}

{% tabs %}
{% tab title="Flutter" %}

```javascript
@override
Widget build(BuildContext context) {
  return Scaffold(
    body: WebView(
    initialUrl: 'https://checkout.lahza.io/akeiIjNbv23',
    javascriptMode: JavascriptMode.unrestricted,
    userAgent: 'Flutter;Webview',
    navigationDelegate: (navigation){
      if(navigation.url == 'https://api.lahza.io/close'){
        Navigator.of(context).pop(); //close webview
      }
      if(navigation.url == "https://example.ps/callback"){
         Navigator.of(context).pop(); //close webview 
      }
      return NavigationDecision.navigate;
    },
  ),
  );
}
```

{% endtab %}

{% tab title="React Native" %}

```jsx
import React from 'react';
import { WebView } from 'react-native-webview';


export default function App() {

  const authorization_url = 'https://checkout.lahza.io/kjIjs82H';
  const callback_url = 'https://yourcallback.com';

  onNavigationStateChange = state => {
 
    const { url } = state;

    if (!url) return;

    if (url === callback_url) {
// get transaction reference from url and verify transaction, then redirect
      const redirectTo = 'window.location = "' + callback_url + '"';
      this.webview.injectJavaScript(redirectTo);
    }
		
  if(url === 'https://api.lahza.io/close') {
      // handle webview removal
      // You can either unmount the component, or
      // Use a navigator to pop off the view
    }
  };

  return (
    <WebView 
      source={{ uri: authorization_url }}
      style={{ marginTop: 40 }}
      onNavigationStateChange={ this.onNavigationStateChange }
    />
  );
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
class MainActivity : AppCompatActivity() {

    private val authorizationUrl: String
        get() = "https://checkout.lahza.io/jdh2723JJS"
    private val callbackUrl: String
        get() = "https://yourcallback.com"

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
    }

    @SuppressLint("SetJavaScriptEnabled")
    private fun loadCheckout() {
        val webView: WebView = findViewById(R.id.webview)
        webView.settings.apply {
            javaScriptEnabled = true
            javaScriptCanOpenWindowsAutomatically = true
            domStorageEnabled = true
        }

        webView.webViewClient = object:  WebViewClient() {
            override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
                val url: Uri? = request?.url

                if (url?.host == callbackUrl) {
                    return true
                } else if (url.toString() == "https://api.lahza.io/close") {
                    finish()
                    return false
                }
                
                return super.shouldOverrideUrlLoading(view, request)
            }
        }

        webView.loadUrl(authorizationUrl)
    }
}
```

{% endtab %}

{% tab title="Swift" %}

```swift
import WebKit

class CheckoutViewController: UIViewController, WKNavigationDelegate {
  
	..........
	
  // This is a WKNavigationDelegate func we can use to handle redirection
    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, 
        decisionHandler: @escaping ((WKNavigationActionPolicy) -> Void)  {

    if let url = navigationAction.request.url {
       
    /*
        We can check here if the 3DS flow is done by checking
        the redirected URl is the one for 3DS after completition
    */
      if url.absoluteString == "https://api.lahza.io/close"{
        decisionHandler(.cancel)
      }
      else{
        decisionHandler(.allow)
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}

### Keep in mind

* When using the WebView, listen for URL redirects to handle customer actions during the payment process.
* Successful payments in the WebView are redirected to the callback URL specified in your Lahza dashboard, but you can override it with a custom callback URL.
* If you have webhooks set up, Lahza will send a charge.success event to your webhook URL. Utilize this event in your backend to deliver value to the customer.
* In the frontend, after the WebView redirects to the callback URL, make a call to the Verify Transaction API to confirm the payment status.
* For card transactions with 3DS authentication, the page doesn't close automatically in the WebView as it does in a web browser.
* Implement a workaround by having the WebView listen for the redirection to a specific URL (e.g., [https://api.lahza.io/close](https://api.lahza.co/close)).
* Once the WebView detects this URL, continue processing as usual by checking the callback function and any other necessary processes.

By following these steps, you can ensure proper handling of customer actions, confirmation of payment status, and handling of card transactions with 3DS authentication in the WebView.


# Go Live Checklist

Prepare your e-commerce site for a seamless payment gateway integration with our essential checklist. Ensure a secure, user-friendly, and compliant transaction process for your customers.

* [x] A valid website URL is necessary for the Gateway, whereas for Payment Links, no URL is required.
* [x] Ensure the installation of an SSL Certificate with a minimum validity of one year.
* [x] Display a prominent company logo on the website's footer, along with Verified by Visa and MasterCard Secure Code logos.
* [x] Provide easily accessible contact information on the website for customer inquiries and support.
* [x] Develop a comprehensive privacy policy.
* [x] Establish clear policies for refunds, returns, and shipping of merchandise/services.
* [x] Implement a user-friendly shopping method, such as a shopping cart, for efficient item selection before checkout.
* [x] Include fields in the checkout process for detailed buyer address information, ensuring accurate delivery of purchased items.
* [x] Omit any fields unrelated to the payment process.
* [x] Avoid requiring buyers to input card details directly on the website; this should occur during redirection to the e-commerce portal.
* [x] Generate and email a detailed purchase slip to the buyer after each transaction, including:
  * [x] Merchant/Company name, physical address, and website URL.&#x20;
  * [x] Transaction date.&#x20;
  * [x] Description and quantities of products/services.&#x20;
  * [x] Order ID.&#x20;
  * [x] Authorization code of the card used.&#x20;
  * [x] Last four digits of the card.&#x20;
  * [x] Payment method (Visa or MasterCard).&#x20;
  * [x] Buyer's address (for tangible products).&#x20;
  * [x] Total amount, including item prices, quantity totals, VAT (if applicable), shipping costs, and any additional charges.&#x20;
  * [x] Expected delivery date.&#x20;
  * [x] Company's policy on refunds and cancellations (optional).
* [x] Implement anti-spam and bot prevention technologies like reCAPTCHA in the checkout form.


# Overview

Learn how to create enjoyable face-to-face payment experiences using Bank of Palestine's Terminal.

## Get Started

### Send Payment Request

{% hint style="info" %}
You can take a payment in person by starting a payment on your Point of Sale system, website, or mobile app, and finishing it on the Bank of Palestine Terminal.
{% endhint %}

### Introduction <a href="#introduction" id="introduction"></a>

The Bank of Palestine Terminal helps you connect in-person payments with your POS system. You can start a payment request from the server of your point of sale system, website, or mobile app, and finish the payment on our Terminal.&#x20;

The setup involves three steps:

1. Send the payment request to the Terminal.
2. Wait for a payment notification

### Send the payment request to the Terminal

When a customer is ready to pay after choosing their items, you start a payment request from your app to our `payment-request` endpoint.

### Listen to notifications <a href="#listen-to-notifications" id="listen-to-notifications"></a>


