{
  "schemaVersion": "1.0",
  "item": {
    "slug": "paypal",
    "name": "PayPal",
    "source": "tencent",
    "type": "skill",
    "category": "安全合规",
    "sourceUrl": "https://clawhub.ai/ivangdavila/paypal",
    "canonicalUrl": "https://clawhub.ai/ivangdavila/paypal",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/paypal",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=paypal",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "SKILL.md",
      "patterns.md",
      "webhooks.md"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-23T16:43:11.935Z",
      "expiresAt": "2026-04-30T16:43:11.935Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=4claw-imageboard",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=4claw-imageboard",
        "contentDisposition": "attachment; filename=\"4claw-imageboard-1.0.1.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/paypal"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/paypal",
    "agentPageUrl": "https://openagent3.xyz/skills/paypal/agent",
    "manifestUrl": "https://openagent3.xyz/skills/paypal/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/paypal/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "When to Use",
        "body": "User needs to integrate PayPal REST API for payments, subscriptions, or payouts. Agent handles checkout flows, webhook verification, OAuth token management, and dispute workflows."
      },
      {
        "title": "Quick Reference",
        "body": "TopicFileCode patternspatterns.mdWebhook eventswebhooks.md"
      },
      {
        "title": "1. Environment URLs are Different",
        "body": "Sandbox: api.sandbox.paypal.com\nProduction: api.paypal.com\nAsk which environment BEFORE generating code\nCredentials are environment-specific — never mix"
      },
      {
        "title": "2. OAuth Token Management",
        "body": "// Token expires ~8 hours — handle refresh\nconst getToken = async () => {\n  const res = await fetch('https://api.paypal.com/v1/oauth2/token', {\n    method: 'POST',\n    headers: {\n      'Authorization': `Basic ${Buffer.from(`${clientId}:${secret}`).toString('base64')}`,\n      'Content-Type': 'application/x-www-form-urlencoded'\n    },\n    body: 'grant_type=client_credentials'\n  });\n  return res.json(); // { access_token, expires_in }\n};\n\nNever hardcode tokens. Implement refresh logic."
      },
      {
        "title": "3. Webhook Verification is Mandatory",
        "body": "PayPal webhooks MUST be verified via API call — not simple HMAC:\n\n// POST /v1/notifications/verify-webhook-signature\nconst verification = await fetch('https://api.paypal.com/v1/notifications/verify-webhook-signature', {\n  method: 'POST',\n  headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },\n  body: JSON.stringify({\n    auth_algo: headers['paypal-auth-algo'],\n    cert_url: headers['paypal-cert-url'],\n    transmission_id: headers['paypal-transmission-id'],\n    transmission_sig: headers['paypal-transmission-sig'],\n    transmission_time: headers['paypal-transmission-time'],\n    webhook_id: WEBHOOK_ID,\n    webhook_event: body\n  })\n});\n// verification_status === 'SUCCESS'"
      },
      {
        "title": "4. CAPTURE vs AUTHORIZE — Ask First",
        "body": "IntentBehaviorCAPTURECharges immediately on approvalAUTHORIZEReserves funds, capture later (up to 29 days)\n\nChanging intent after integration breaks the entire flow."
      },
      {
        "title": "5. Server-Side Validation — Never Trust Client",
        "body": "// After client approves, VERIFY on server before fulfillment\nconst order = await fetch(`https://api.paypal.com/v2/checkout/orders/${orderId}`, {\n  headers: { 'Authorization': `Bearer ${token}` }\n}).then(r => r.json());\n\n// Validate ALL of these:\nif (order.status !== 'APPROVED') throw new Error('Not approved');\nif (order.purchase_units[0].amount.value !== expectedAmount) throw new Error('Amount mismatch');\nif (order.purchase_units[0].amount.currency_code !== expectedCurrency) throw new Error('Currency mismatch');\nif (order.purchase_units[0].payee.merchant_id !== YOUR_MERCHANT_ID) throw new Error('Wrong merchant');"
      },
      {
        "title": "6. Idempotency in Webhooks",
        "body": "PayPal may send the same webhook multiple times:\n\nconst processed = await db.webhooks.findOne({ eventId: body.id });\nif (processed) return res.status(200).send('Already processed');\nawait db.webhooks.insert({ eventId: body.id, processedAt: new Date() });\n// Now process the event"
      },
      {
        "title": "7. Currency Decimal Rules",
        "body": "Some currencies have NO decimal places:\n\nCurrencyDecimalsExampleUSD, EUR2\"10.50\"JPY, TWD0\"1050\" (NOT \"1050.00\")\n\nSending \"10.50\" for JPY = API error."
      },
      {
        "title": "Common Traps",
        "body": "IPN vs Webhooks — IPN is legacy. Use Webhooks for new integrations. Never mix.\nOrder states — CREATED → APPROVED → COMPLETED (or VOIDED). Handle ALL states, not just happy path.\nDecimal confusion — PayPal uses strings for amounts (\"10.50\"), not floats. Some currencies forbid decimals.\nSandbox rate limits — Lower than production. Don't assume prod will fail the same way.\nPayout vs Payment — Payouts API is separate. Don't confuse sending money (Payouts) with receiving (Orders)."
      }
    ],
    "body": "When to Use\n\nUser needs to integrate PayPal REST API for payments, subscriptions, or payouts. Agent handles checkout flows, webhook verification, OAuth token management, and dispute workflows.\n\nQuick Reference\nTopic\tFile\nCode patterns\tpatterns.md\nWebhook events\twebhooks.md\nCore Rules\n1. Environment URLs are Different\nSandbox: api.sandbox.paypal.com\nProduction: api.paypal.com\nAsk which environment BEFORE generating code\nCredentials are environment-specific — never mix\n2. OAuth Token Management\n// Token expires ~8 hours — handle refresh\nconst getToken = async () => {\n  const res = await fetch('https://api.paypal.com/v1/oauth2/token', {\n    method: 'POST',\n    headers: {\n      'Authorization': `Basic ${Buffer.from(`${clientId}:${secret}`).toString('base64')}`,\n      'Content-Type': 'application/x-www-form-urlencoded'\n    },\n    body: 'grant_type=client_credentials'\n  });\n  return res.json(); // { access_token, expires_in }\n};\n\n\nNever hardcode tokens. Implement refresh logic.\n\n3. Webhook Verification is Mandatory\n\nPayPal webhooks MUST be verified via API call — not simple HMAC:\n\n// POST /v1/notifications/verify-webhook-signature\nconst verification = await fetch('https://api.paypal.com/v1/notifications/verify-webhook-signature', {\n  method: 'POST',\n  headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },\n  body: JSON.stringify({\n    auth_algo: headers['paypal-auth-algo'],\n    cert_url: headers['paypal-cert-url'],\n    transmission_id: headers['paypal-transmission-id'],\n    transmission_sig: headers['paypal-transmission-sig'],\n    transmission_time: headers['paypal-transmission-time'],\n    webhook_id: WEBHOOK_ID,\n    webhook_event: body\n  })\n});\n// verification_status === 'SUCCESS'\n\n4. CAPTURE vs AUTHORIZE — Ask First\nIntent\tBehavior\nCAPTURE\tCharges immediately on approval\nAUTHORIZE\tReserves funds, capture later (up to 29 days)\n\nChanging intent after integration breaks the entire flow.\n\n5. Server-Side Validation — Never Trust Client\n// After client approves, VERIFY on server before fulfillment\nconst order = await fetch(`https://api.paypal.com/v2/checkout/orders/${orderId}`, {\n  headers: { 'Authorization': `Bearer ${token}` }\n}).then(r => r.json());\n\n// Validate ALL of these:\nif (order.status !== 'APPROVED') throw new Error('Not approved');\nif (order.purchase_units[0].amount.value !== expectedAmount) throw new Error('Amount mismatch');\nif (order.purchase_units[0].amount.currency_code !== expectedCurrency) throw new Error('Currency mismatch');\nif (order.purchase_units[0].payee.merchant_id !== YOUR_MERCHANT_ID) throw new Error('Wrong merchant');\n\n6. Idempotency in Webhooks\n\nPayPal may send the same webhook multiple times:\n\nconst processed = await db.webhooks.findOne({ eventId: body.id });\nif (processed) return res.status(200).send('Already processed');\nawait db.webhooks.insert({ eventId: body.id, processedAt: new Date() });\n// Now process the event\n\n7. Currency Decimal Rules\n\nSome currencies have NO decimal places:\n\nCurrency\tDecimals\tExample\nUSD, EUR\t2\t\"10.50\"\nJPY, TWD\t0\t\"1050\" (NOT \"1050.00\")\n\nSending \"10.50\" for JPY = API error.\n\nCommon Traps\nIPN vs Webhooks — IPN is legacy. Use Webhooks for new integrations. Never mix.\nOrder states — CREATED → APPROVED → COMPLETED (or VOIDED). Handle ALL states, not just happy path.\nDecimal confusion — PayPal uses strings for amounts (\"10.50\"), not floats. Some currencies forbid decimals.\nSandbox rate limits — Lower than production. Don't assume prod will fail the same way.\nPayout vs Payment — Payouts API is separate. Don't confuse sending money (Payouts) with receiving (Orders)."
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/ivangdavila/paypal",
    "publisherUrl": "https://clawhub.ai/ivangdavila/paypal",
    "owner": "ivangdavila",
    "version": "1.0.0",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/paypal",
    "downloadUrl": "https://openagent3.xyz/downloads/paypal",
    "agentUrl": "https://openagent3.xyz/skills/paypal/agent",
    "manifestUrl": "https://openagent3.xyz/skills/paypal/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/paypal/agent.md"
  }
}