| @@ -125,9 +125,9 @@ | ||
| 125 | 125 | $message = ! empty( $session->error->message ) |
| 126 | 126 | ? sanitize_text_field( $session->error->message ) |
| 127 | 127 | : __( 'There was a problem connecting to the Stripe API endpoint.', 'better-payment' ); |
| 128 | 128 | |
| 129 | - return new \WP_Error( 'stripe_error', $message ); | |
| 129 | + return new \WP_Error( 'stripe_error', $message, self::error_details( $session->error ) ); | |
| 130 | 130 | } |
| 131 | 131 | |
| 132 | 132 | if ( empty( $session->id ) ) { |
| 133 | 133 | return new \WP_Error( 'stripe_error', __( 'Unexpected response from the Stripe API endpoint.', 'better-payment' ) ); |
| @@ -283,9 +283,14 @@ | ||
| 283 | 283 | $message = ! empty( $object->error->message ) |
| 284 | 284 | ? sanitize_text_field( $object->error->message ) |
| 285 | 285 | : __( 'There was a problem connecting to the Stripe API endpoint.', 'better-payment' ); |
| 286 | 286 | |
| 287 | - return new \WP_Error( 'stripe_error', $message ); | |
| 287 | + // The error code/type travels as WP_Error DATA, never as its code: | |
| 288 | + // every existing consumer matches on 'stripe_error' and on the | |
| 289 | + // message. Callers that must tell a declined card from a | |
| 290 | + // mis-configured Stripe account read it from here (see | |
| 291 | + // WooCommerce\Subscriptions::renewal_failure_is_configuration()). | |
| 292 | + return new \WP_Error( 'stripe_error', $message, self::error_details( $object->error ) ); | |
| 288 | 293 | } |
| 289 | 294 | |
| 290 | 295 | if ( empty( $object->id ) ) { |
| 291 | 296 | return new \WP_Error( 'stripe_error', __( 'Unexpected response from the Stripe API endpoint.', 'better-payment' ) ); |
| @@ -291,6 +296,30 @@ | ||
| 291 | 296 | return new \WP_Error( 'stripe_error', __( 'Unexpected response from the Stripe API endpoint.', 'better-payment' ) ); |
| 292 | 297 | } |
| 293 | 298 | |
| 294 | 299 | return $object; |
| 300 | + } | |
| 301 | + | |
| 302 | + /** | |
| 303 | + * Pure: the `type`/`code`/`decline_code` of a Stripe error payload, as | |
| 304 | + * the WP_Error data array. Keys are always present (empty strings when | |
| 305 | + * Stripe sent none), so a consumer never has to isset() its way through. | |
| 306 | + * | |
| 307 | + * @since 2.4.0 | |
| 308 | + * | |
| 309 | + * @param mixed $error Stripe error object. | |
| 310 | + * @return array `type`, `code`, `decline_code`. | |
| 311 | + */ | |
| 312 | + public static function error_details( $error ) { | |
| 313 | + $read = static function ( $key ) use ( $error ) { | |
| 314 | + return is_object( $error ) && isset( $error->{$key} ) && is_scalar( $error->{$key} ) | |
| 315 | + ? sanitize_text_field( (string) $error->{$key} ) | |
| 316 | + : ''; | |
| 317 | + }; | |
| 318 | + | |
| 319 | + return array( | |
| 320 | + 'type' => $read( 'type' ), | |
| 321 | + 'code' => $read( 'code' ), | |
| 322 | + 'decline_code' => $read( 'decline_code' ), | |
| 323 | + ); | |
| 295 | 324 | } |
| 296 | 325 | } |