如何在PHP中捕获Stripe致命错误?

6
我正在使用Stripe.js元素启动一个应用程序。使用Stripe提供的测试卡号时,没有问题,一切按预期工作。现在我专注于错误处理。当使用测试卡4000 0000 0000 0002处理故意拒绝的卡时,会出现以下错误:Fatal error: Uncaught Stripe\Error\Card: Your card was declined. in C:\Apache24\htdocs...\vendor\stripe\stripe-php\lib\ApiRequestor.php:128 from API request {token}.... 我假设这不是PHP致命错误(无法在try/catch块中处理),所以我搜索并找到了this example,并将其实施到我的代码中,如下所示:
\Stripe\Stripe::setApiKey(self::APIKEY);

    $charge_arr = array(
        "key" => value, etc ... )

        try {               
            $charge = \Stripe\Charge::create($charge_arr);
            if($charge->paid == true) { 
                echo '<br>the card was successfully charged!';
            }

        } catch (\Stripe\Error\Base $e) {
            echo '<br>base';
            echo ($e->getMessage());

        } catch (\Stripe\Error\Card $e) {
            $body = $e->getJsonBody();
            $err  = $body['error'];

            print('Status is:' . $e->getHttpStatus() . "\n");
            print('Type is:' . $err['type'] . "\n");
            print('Code is:' . $err['code'] . "\n");                

            echo ($e->getMessage()); 

        } catch (\Stripe\Error\Authentication $e) {
            echo '<br>Auth';                
            // a good one to catch
        } catch (\Stripe\Error\InvalidRequest $e) {
            echo '<br>Invalid Request';             
            // and catch this one just in case
        } catch (Exception $e) {
            //catch any non-stripe exceptions
        }

然而,这并没有捕获错误。消息继续显示为之前没有catch块的情况下。
有什么线索可以解释我为什么会收到致命错误?当然,我预计卡会被拒绝,但我期望在try/catch块中处理拒绝的结果。
编辑
我应该补充说明,我正在使用composer来包含Stripe php库。
进一步说明:可能不相关,但是所有应该被拒绝的测试卡都会出现致命错误消息。每张卡片的拒绝原因在错误消息中清楚地说明(不是逐字)如邮政编码验证失败、CVC不正确、卡过期等。
正如@Ywain所指出的那样,这实际上不是try/catch块的问题。致命错误是由第一个交易的charge生成的。换句话说,对\Stripe\Charge的调用应返回具有适当标志或字段值的JSON数组,而不是致命错误。

\Stripe\Error\Base 是所有 Stripe 错误的父类,因此如果您首先捕获它,则不会执行任何其他 \Stripe\Error\...\ 捕获子句。您应该在最后第二个位置(在 Exception 之前但在所有其他捕获子句之后)才捕获它。话虽如此,如果尽管出现错误消息但没有执行任何捕获子句,则最可能的解释是运行的代码与您查看并复制到此处的代码不同。 - Ywain
@Ywain - 我已经尝试了很多不同的代码方式。即使没有任何try/catch,我也得到了相同的结果。所以你提出了一个很好的观点:虽然try/catch块可能不符合“规范”,但这并不是问题所在。我将重新编辑帖子以反映这一点。有什么线索吗? - globalSchmidt
@globalSchmidt,你有解决这个问题的办法吗?我也遇到了完全相同的问题! - Lachie
2个回答

5

Stripe在交易过程中可能会抛出许多异常。他们的API有优秀的示例代码,提供了捕获Stripe API可能抛出的各种异常的模板。

try {
  // Use Stripe's library to make requests...
} catch(\Stripe\Error\Card $e) {
  // Since it's a decline, \Stripe\Error\Card will be caught
  $body = $e->getJsonBody();
  $err  = $body['error'];

  print('Status is:' . $e->getHttpStatus() . "\n");
  print('Type is:' . $err['type'] . "\n");
  print('Code is:' . $err['code'] . "\n");
  // param is '' in this case
  print('Param is:' . $err['param'] . "\n");
  print('Message is:' . $err['message'] . "\n");
} catch (\Stripe\Error\RateLimit $e) {
  // Too many requests made to the API too quickly
} catch (\Stripe\Error\InvalidRequest $e) {
  // Invalid parameters were supplied to Stripe's API
} catch (\Stripe\Error\Authentication $e) {
  // Authentication with Stripe's API failed
  // (maybe you changed API keys recently)
} catch (\Stripe\Error\ApiConnection $e) {
  // Network communication with Stripe failed
} catch (\Stripe\Error\Base $e) {
  // Display a very generic error to the user, and maybe send
  // yourself an email
} catch (Exception $e) {
  // Something else happened, completely unrelated to Stripe
}

请记住,一旦捕获异常,除非您采取措施特别结束脚本或更改执行路径,否则您的脚本将继续运行其余代码,毫不知情已经引发了异常。考虑这个过于简单的例子。

try {
    $charge = \Stripe\Charge::create($charge_arr);
} catch (\Stripe\Error\Card $e) {
    echo("I had an error!");
}
echo ("I completed Successfully");

卡被拒绝后,API抛出\Stripe\Error\Card。接下来你会收到以下输出。

我遇到了一个错误!我已经完成了。

如果您的执行路径中有多个API调用,则此事很重要。您可能完全捕获了抛出的异常,但是后面的调用可能会抛出另一个您没有预料到的异常。您应该将所有可能引发异常的API调用都包装在try / catch中。如果您期望从之前的调用中捕获错误以阻止其他代码执行,则还需要注意执行路径。以下是一个过于简单的示例。
try {
    $charge = \Stripe\Charge::create($charge_arr);
    $declined = false;
} catch (\Stripe\Error\Card $e) {
    echo("I had an error!");
    $declined = true;
}
if(!$declined) {
    echo ("I completed Successfully");
}

这正是我开始的地方... 这些都没有捕获到“致命错误”。这非常令人沮丧。 - globalSchmidt
你有没有其他的调用 Stripes API 的代码不在 try/catch 块中?我提到这个原因是因为前几天我遇到了同样的问题。他们捕获了第一个调用的异常,但从未在错误上退出脚本,这导致其后的脚本在进行第二个调用时出错。 - Symeon Quimby
好知道,但不是。这是我开始关注的第一个错误情况。没有其他调用有try/catch块。 - globalSchmidt
@SymeonQuimby 我正在使用您在此问题中提供的最后一个示例,但当变量!$declined从“false”变为“true”时,它显示以下错误消息:未定义的变量:declined - user8880927

1
我认为你需要捕获不同类型的抛出错误。
\Stripe\Stripe::setApiKey(self::APIKEY);

    $charge_arr = array(
        "key" => value, etc ... )

    try {

        $charge = \Stripe\Charge::create($charge_arr);

    } catch (\Stripe\Error\Base $e) {       
        echo ($e->getMessage());
    } catch (\Stripe\Error\Card $e) {
        echo ($e->getMessage()); // I think this is the missing one
    } catch (\Stripe\Error\Authentication $e) {
        // a good one to catch
    } catch (\Stripe\Error\InvalidRequest $e) {
        // and catch this one just in case
    } catch (Exception $e) {
        //catch any non-stripe exceptions
    }

好眼力!不過沒有改變。仍然收到相同的致命錯誤訊息。 - globalSchmidt
这可能是一个愚蠢的问题,但路径 \Stripe\Error* 是有效的吗?想确保它能够看到那些类。 - John C
好的,Stripe是通过composer包管理器引入的,所以我没有移动或触碰任何文件。我认为(因为在Stripe文档中有提到)\Stripe\Error{whatever]是有效的引用。该错误确实涉及到stripe\stripe-php\lib\ApiRequestor.php的第128行,该行处理类似于以下方式的402类型错误:case 402: return new Error\Card($msg, $param, $code, $rcode, $rbody, $resp, $rheaders);除了说“Error\Card”是一个东西之外,并没有什么帮助。 - globalSchmidt
根据源代码\Stripe\Error\Card是正确的异常类型。也许你应该发布你的完整更新代码? - fubar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接