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
// 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 = '[email protected]';
$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
// 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

Last updated