# Malum Checkout Form

This guide shows how to integrate a simple checkout form with the Malum Payment API. It now supports optional product presentation fields that render on the Malum checkout page, along with an additional product signature for integrity.

### Prerequisites

* Malum Business ID
* Malum Private Key
* Ability to edit your site's PHP backend

### Core Flow

1. Collect the required parameters.
2. Create a signed message to authenticate the payment request.
3. Optionally include product presentation fields and create a signed product hash.
4. Post a form to Malum to start the checkout.
5. Handle the webhook notification and user redirects.

### Required Parameters

* `amount` numeric string or integer representing the price in major currency units
* `currency` ISO 4217 code such as `USD`, `EUR`
* `webhook_url` URL on your server to receive payment results
* `success_url` where to redirect the buyer after a successful payment
* `cancel_url` where to redirect if the buyer cancels or the payment fails
* `business_id` your Malum business identifier
* `customer_email` buyer email
* `buyer_pays_fees` `0` or `1`
* `metadata` optional opaque string you receive back
* `signed_message` authentication signature described below

### Optional Product Presentation Parameters

These fields allow Malum to display product details on the hosted checkout.

* `product_markdown` free form Markdown that describes the product
* `product_title` short plain title
* `product_image` absolute URL to a product image
* `product_description` short plain summary
* `product_link` absolute URL to view the product on your site
* `signed_product_hash` integrity signature for the five product fields

### Signatures

#### Payment request signature

```
$signed_message = md5(
    $amount
  . $currency
  . $webhook_url
  . $success_url
  . $cancel_url
  . $customer_email
  . $buyer_pays_fees
  . $metadata
  . $business_id
  . $private_key
);
```

#### Product presentation signature

```
$signed_product_hash = md5(
    $product_markdown
  . $product_title
  . $product_image
  . $product_description
  . $product_link
  . $private_key
);
```

### Quick Test Values

Use these while testing. Replace with real values for production.

```php
<?php
// Test values
$amount = 1;
$currency = 'USD';
$webhook_url = 'https://example.com';
$success_url = 'https://example.com';
$cancel_url = 'https://example.com';
$business_id = 'TEST';
$private_key = 'sec_TEST';
$metadata = '';
$customer_email = 'test@example.com';
$buyer_pays_fees = 0;

// Sign the payment request
$signed_message = md5(
    $amount . $currency . $webhook_url . $success_url . $cancel_url .
    $customer_email . $buyer_pays_fees . $metadata . $business_id . $private_key
);

// Optional product presentation
$product_markdown = 'This is a test product.';
$product_title = 'Test Product';
$product_image = 'https://via.placeholder.com/150';
$product_description = 'This is a description for the test product.';
$product_link = 'https://example.com/product';

// Sign the product block
$signed_product_hash = md5(
    $product_markdown . $product_title . $product_image .
    $product_description . $product_link . $private_key
);
?>
```

### Full PHP Example

This example posts the required fields and the optional product fields. Inputs that can contain special characters are HTML escaped.

```php
<?php
// Values from the Quick Test Values section above
// ...
?>
<form method="POST" action="https://malum.co/api/v3/checkout/form">
    <input type="hidden" name="amount" value="<?php echo $amount; ?>">
    <input type="hidden" name="currency" value="<?php echo $currency; ?>">
    <input type="hidden" name="webhook_url" value="<?php echo htmlspecialchars($webhook_url, ENT_QUOTES); ?>">
    <input type="hidden" name="success_url" value="<?php echo htmlspecialchars($success_url, ENT_QUOTES); ?>">
    <input type="hidden" name="cancel_url" value="<?php echo htmlspecialchars($cancel_url, ENT_QUOTES); ?>">
    <input type="hidden" name="customer_email" value="<?php echo htmlspecialchars($customer_email, ENT_QUOTES); ?>">
    <input type="hidden" name="buyer_pays_fees" value="<?php echo (int)$buyer_pays_fees; ?>">
    <input type="hidden" name="metadata" value="<?php echo htmlspecialchars($metadata, ENT_QUOTES); ?>">
    <input type="hidden" name="business_id" value="<?php echo htmlspecialchars($business_id, ENT_QUOTES); ?>">
    <input type="hidden" name="signed_message" value="<?php echo $signed_message; ?>">

    <!-- Optional product presentation fields -->
    <input type="hidden" name="product_markdown" value="<?php echo htmlspecialchars($product_markdown, ENT_QUOTES); ?>">
    <input type="hidden" name="product_title" value="<?php echo htmlspecialchars($product_title, ENT_QUOTES); ?>">
    <input type="hidden" name="product_image" value="<?php echo htmlspecialchars($product_image, ENT_QUOTES); ?>">
    <input type="hidden" name="product_description" value="<?php echo htmlspecialchars($product_description, ENT_QUOTES); ?>">
    <input type="hidden" name="product_link" value="<?php echo htmlspecialchars($product_link, ENT_QUOTES); ?>">
    <input type="hidden" name="signed_product_hash" value="<?php echo $signed_product_hash; ?>">

    <button type="submit">Pay Now</button>
</form>
```

### Webhook Handling

Your `webhook_url` will receive a server to server POST from Malum with the payment status and identifiers. On your endpoint you should:

1. Read the payload and Malum signature header if provided.
2. Verify the payload authenticity according to Malum documentation, for example by reconstructing a signature or by fetching the payment status from Malum using your credentials.
3. Validate that the `amount`, `currency`, `metadata`, and any product details match what you expect for the order.
4. Mark the order as paid only after successful verification and idempotently process repeated notifications.

### Redirect URLs

* `success_url` shown after a successful payment
* `cancel_url` shown if the buyer cancels or payment fails

### Security Notes

* Never expose your `private_key` to the client side. Keep it in server side configuration.
* Always rebuild `signed_message` and, if used, `signed_product_hash` on the server.
* Escape any user supplied values placed in hidden inputs using `htmlspecialchars` as shown above.
* Treat webhook as the source of truth for fulfillment. The browser redirect can be spoofed.

### Troubleshooting

* Signature mismatch. Ensure parameter order in your hashes matches the examples exactly and that you include `buyer_pays_fees` in the payment signature.
* Wrong redirects. Confirm the exact values of `success_url` and `cancel_url` that you hashed are the same ones you post.
* Duplicate field issues. Include `signed_product_hash` only once in the form.

### Production Checklist

* Replace all test values with real ones
* Use HTTPS everywhere
* Log request IDs and verify signatures
* Make webhook processing idempotent
* Store and show your order number in `metadata` for easier reconciliation


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.malum.co/transaction-api/malum-checkout-form.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
