openvibers/openvibe.live Commits

PowerChat donate links: send viewers back; explain isTest skips in the log

OP OpenVibers committed 27 Aug 2026 · 9330205c2d · 2 files, +14 −3

The "tip went through but nothing happened" report was two things working
as designed but invisibly: (1) donate links never asked for the return
redirect (only purchases/subs did), so PowerChat left the viewer on the
tip page — donors now carry app_redirect_uri too; (2) the tip was an
isTest delivery (a dev PowerChat with no payout provider confirms every
checkout as a test) and fulfillment correctly skipped it — the skip log
now names the checkout ref and the powerchat_allow_test_fulfillment
toggle so the next person diagnoses it in one journal line.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017AyeSFu7qdLdR64Zv99QNm

Changes

ddiff --git a/server/integrations/powerchat-checkout.js b/server/integrations/powerchat-checkout.js
iindex a47221bd..3dcd6464 100644
---- a/server/integrations/powerchat-checkout.js
++++ b/server/integrations/powerchat-checkout.js
@@@ -119,11 +119,15 @@ function buildSubscribeLink(order, streamerUserId, { autoRenew = 0 } = {}) {
// the viewer's free choice — donations are never pinned.
function buildDonateLink(streamerUserId, donorUserId, { goalId = null } = {}) {
const purpose = goalId ? `goal:${goalId}` : 'donation';
++ // returnTo: donors get auto-redirected back to our confirmation page after the
++ // checkout too — without app_redirect_uri PowerChat only offers a plain
++ // "Return to app" link and viewers just sat on the tip page.
++ const opts = { purpose, returnTo: true };
const direct = _checkoutConn(streamerUserId);
-- if (direct) return { url: tipLinkFor(direct.powerchat_username, '', { purpose }), mode: 'direct' };
++ if (direct) return { url: tipLinkFor(direct.powerchat_username, '', opts), mode: 'direct' };
const site = getSiteAccount();
if (!site) return null;
-- return { url: tipLinkFor(site.username, `pcdon:${streamerUserId}:${donorUserId || 0}`, { purpose }), mode: 'site' };
++ return { url: tipLinkFor(site.username, `pcdon:${streamerUserId}:${donorUserId || 0}`, opts), mode: 'site' };
}
// ── Webhook fulfillment ──────────────────────────────────────
ddiff --git a/server/integrations/powerchat-webhook.js b/server/integrations/powerchat-webhook.js
iindex 75f7be8d..095061df 100644
---- a/server/integrations/powerchat-webhook.js
++++ b/server/integrations/powerchat-webhook.js
@@@ -206,7 +206,14 @@ function processEvent(envelope) {
// data.isTest = no money moved (dashboard test fires, free test method, no payout
// provider). Never credit those — unless the owner explicitly enabled test
// fulfillment for dev/sandbox, where every delivery is isTest.
-- if (data.isTest && !_testFulfillmentAllowed()) { console.log(`[PowerChat] test webhook ${envelope.type} verified OK (not credited)`); return; }
++ if (data.isTest && !_testFulfillmentAllowed()) {
++ // Make the skip self-explanatory when it's an attributed checkout — this is the
++ // #1 "my tip went through but nothing credited" question: the PowerChat side
++ // confirmed a TEST checkout (no payout provider / free test method).
++ const ref = data.appExternalRef ? ` (checkout ref ${data.appExternalRef} — no money moved; enable powerchat_allow_test_fulfillment in admin to credit test checkouts in dev)` : '';
++ console.log(`[PowerChat] test webhook ${envelope.type} verified OK (not credited)${ref}`);
++ return;
++ }
if (data.isTest) console.warn(`[PowerChat] processing TEST ${envelope.type} (powerchat_allow_test_fulfillment is ON — no money moved)`);
// App-sourced events are OUR OWN pushes (tips/subs/follows/redemptions we forwarded
// via the platform scopes) echoing back through the webhook — they already happened