{"record":{"id":"0869a5446bb55444","repo":"phacility/phabricator","slug":"unable-to-update-charge-no-stripe-chargeid","errorCode":null,"errorMessage":"Unable to update charge; no Stripe chargeID!","messagePattern":"Unable to update charge; no Stripe chargeID!","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/phortune/provider/PhortuneStripePaymentProvider.php","lineNumber":194,"sourceCode":"\n    $stripe_charge = Stripe_Charge::retrieve($charge_id, $secret_key);\n    $stripe_refund = $stripe_charge->refunds->create($params);\n\n    $id = $stripe_refund->id;\n    if (!$id) {\n      throw new Exception(pht('Stripe refund call did not return an ID!'));\n    }\n\n    $charge->setMetadataValue('stripe.refundID', $id);\n    $charge->save();\n  }\n\n  public function updateCharge(PhortuneCharge $charge) {\n    $this->loadStripeAPILibraries();\n\n    $charge_id = $charge->getMetadataValue('stripe.chargeID');\n    if (!$charge_id) {\n      throw new Exception(\n        pht('Unable to update charge; no Stripe chargeID!'));\n    }\n\n    $secret_key = $this->getSecretKey();\n    $stripe_charge = Stripe_Charge::retrieve($charge_id, $secret_key);\n\n    // TODO: Deal with disputes / chargebacks / surprising refunds.\n\n  }\n\n  private function getPublishableKey() {\n    return $this\n      ->getProviderConfig()\n      ->getMetadataValue(self::STRIPE_PUBLISHABLE_KEY);\n  }\n\n  private function getSecretKey() {\n    return $this","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/phortune/provider/PhortuneStripePaymentProvider.php#L176-L212","documentation":"Exception thrown by PhortuneStripePaymentProvider::updateCharge() when asked to refresh a charge that has no 'stripe.chargeID' metadata. The update path is Stripe_Charge::retrieve($charge_id, ...) — with no stored ID there is nothing to retrieve. The metadata is only written at the end of a successful executeCharge(), so this indicates the charge never completed (or was not a Stripe charge).","triggerScenarios":"A charge-status worker or admin tool calling updateCharge() on a charge in HOLD/FAIL status whose executeCharge() never reached the save; polling a charge created by a different provider through the Stripe provider; metadata missing after a crash between charge creation and persistence.","commonSituations":"Generic pollers that update every open charge regardless of provider; test fixtures with hand-built charges; scripts run against carts stuck mid-checkout after a Stripe outage.","solutions":["Skip charges without 'stripe.chargeID' — their original flow (or its failure) must finish first.","Limit update polling to CHARGED/HOLD charges that carry Stripe metadata and belong to the Stripe provider.","If a real Stripe charge exists (searchable by charge PHID in the description), restore the metadata before polling."],"exampleFix":"// before\n$provider->updateCharge($charge);\n\n// after\n$charge_id = $charge->getMetadataValue('stripe.chargeID');\nif (!strlen((string)$charge_id)) {\n  // No Stripe reference on this charge; nothing to update.\n  return;\n}\n$provider->updateCharge($charge);","handlingStrategy":"validation","validationCode":"// Only update charges that carry a Stripe reference.\nfunction stripe_charge_is_updatable(PhortuneCharge $charge) {\n  return strlen((string) $charge->getMetadataValue('stripe.chargeID')) > 0;\n}\n\nif (!stripe_charge_is_updatable($charge)) {\n  // No Stripe object to retrieve; leave the charge to its own flow.\n  continue;\n}\n$provider->updateCharge($charge);","typeGuard":"function charge_has_stripe_charge_id(PhortuneCharge $charge) {\n  $id = $charge->getMetadataValue('stripe.chargeID');\n  return is_string($id) && strlen($id) > 0;\n}","tryCatchPattern":"try {\n  $provider->updateCharge($charge);\n} catch (Exception $ex) {\n  // Missing chargeID means the charge never completed via Stripe:\n  // log and exclude from future polling instead of retrying.\n  phlog(pht('Cannot update charge %s: %s', $charge->getPHID(), $ex->getMessage()));\n}","preventionTips":["Scope update workers to charges with provider metadata and matching provider.","Treat absent 'stripe.chargeID' as 'not a completed Stripe charge' — skip, don't poll.","Repair interrupted charge flows promptly so half-written charges don't enter the poll set."],"tags":["php","phabricator","phortune","stripe","charge-status","metadata","payments"],"backgroundTag":"missing-payment-metadata","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}