79738783

Date: 2025-08-18 13:33:12
Score: 0.5
Natty:
Report link

onComplete gives you everything you listed, including the donor’s custom message (cm). You just grab it and POST it to your PHP endpoint. Here’s a clean, working example.

Frontend (JS)

<div id="paypal-donate-button-container"></div>

<script src="https://www.paypalobjects.com/donate/sdk/donate-sdk.js" charset="UTF-8"></script>
<script>
  PayPal.Donation.Button({
    env: 'sandbox',                       // switch to 'production' when live
    hosted_button_id: 'YOUR_SANDBOX_HOSTED_BUTTON_ID',
    image: {
      src: 'https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif',
      title: 'PayPal - The safer, easier way to pay online!',
      alt: 'Donate with PayPal button'
    },
    onComplete: function (params) {
      // params contains: tx, st, amt, cc, cm, item_number, item_name
      // Send it to your server
      fetch('/paypal-complete.php', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          tx: params.tx,
          st: params.st,
          amt: params.amt,
          cc: params.cc,
          cm: params.cm,                 // <-- donor’s custom message
          item_number: params.item_number,
          item_name: params.item_name
        })
      })
      .then(() => location.href = '/thank-you.html')
      .catch(() => location.href = '/thank-you.html'); // show thanks either way
    }
  }).render('#paypal-donate-button-container');
</script>

Backend (PHP)

Receives the JSON, stores it, and (recommended) verifies the payment.

<?php
// /paypal-complete.php

// 1) Read JSON payload
$raw = file_get_contents('php://input');
$data = json_decode($raw, true) ?: [];

// 2) Pull fields (with basic safety)
$tx  = $data['tx']  ?? '';
$st  = $data['st']  ?? '';
$amt = $data['amt'] ?? '';
$cc  = $data['cc']  ?? '';
$cm  = $data['cm']  ?? '';           // donor message
$item_number = $data['item_number'] ?? '';
$item_name   = $data['item_name']   ?? '';

// 3) Persist immediately (DB/log) so nothing is lost
// Example log (replace with real DB insert):
file_put_contents(__DIR__ . '/paypal_donations.log',
  date('c') . " | tx=$tx st=$st amt=$amt $cc | item=$item_number/$item_name | cm=" . str_replace(["\n","\r"], ' ', $cm) . PHP_EOL,
  FILE_APPEND
);

// 4) (Recommended) Verify the transaction server-to-server
// Option A: PDT (Payments Data Transfer)
$useSandbox = true; // false in production
$pdtIdentityToken = 'YOUR_PDT_IDENTITY_TOKEN';

if ($tx && $pdtIdentityToken) {
  $endpoint = $useSandbox
    ? 'https://ipnpb.sandbox.paypal.com/cgi-bin/webscr'
    : 'https://ipnpb.paypal.com/cgi-bin/webscr';

  $payload = http_build_query([
    'cmd' => '_notify-synch',
    'tx'  => $tx,
    'at'  => $pdtIdentityToken
  ]);

  $ch = curl_init($endpoint);
  curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => $payload,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HEADER => false,
    CURLOPT_SSL_VERIFYPEER => true,
  ]);

  $res = curl_exec($ch);
  curl_close($ch);

  if ($res !== false && strncmp($res, "SUCCESS", 7) === 0) {
    // Parse key=value lines after the first line
    $lines = explode("\n", $res);
    array_shift($lines);
    $pdt = [];
    foreach ($lines as $line) {
      $parts = explode("=", $line, 2);
      if (count($parts) === 2) $pdt[urldecode($parts[0])] = urldecode($parts[1]);
    }

    // Sanity checks (examples):
    // if (($pdt['payment_status'] ?? '') === 'Completed'
    //   && ($pdt['mc_gross'] ?? '') == $amt
    //   && ($pdt['mc_currency'] ?? '') == $cc
    //   && ($pdt['txn_id'] ?? '') == $tx) { /* mark as verified */ }

    http_response_code(204); // all good
    exit;
  }
}

// If verification isn’t configured yet, still return 202 so your JS can move on.
// (But do not mark donation as "confirmed" in your DB until verified.)
http_response_code(202);

Notes you’ll care about

If you want, I can add a tiny MySQL table + insert snippet so you can save the donation and message cleanly.

Reasons:
  • Blacklisted phrase (0.5): thanks
  • Long answer (-1):
  • Has code block (-0.5):
  • Contains question mark (0.5):
  • Low reputation (1):
Posted by: Alex