# 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
