← All changes
|
includes/square/gateway/class-charitable-square-webhook-processor.php
+52
-7
1.8.11.3
→
1.8.13
View file →
| @@ -962,9 +962,19 @@ | ||
| 962 | 962 | error_log( 'Square Webhook - Transaction ID: ' . $transaction_id ); |
| 963 | 963 | // phpcs:enable |
| 964 | 964 | } |
| 965 | 965 | |
| 966 | - $refund_amount = $charge['refund']['amount_money']['amount']; | |
| 966 | + $refund_amount = $charge['refund']['amount_money']['amount']; | |
| 967 | + $refund_currency = $charge['refund']['amount_money']['currency']; | |
| 968 | + | |
| 969 | + // Square sends amounts in the smallest currency unit (cents for | |
| 970 | + // USD, yen for JPY, etc.). process_refund() expects the site's | |
| 971 | + // display unit, so divide by 100 unless this is a zero-decimal | |
| 972 | + // currency. | |
| 973 | + if ( ! Charitable_Square_Gateway_Processor::is_zero_decimal_currency( $refund_currency ) ) { | |
| 974 | + $refund_amount = $refund_amount / 100; | |
| 975 | + } | |
| 976 | + | |
| 967 | 977 | if ( charitable_is_debug( 'square' ) ) { |
| 968 | 978 | // phpcs:disable |
| 969 | 979 | error_log( 'Square Webhook - Refund Amount: ' . $refund_amount ); |
| 970 | 980 | // phpcs:enable |
| @@ -1449,15 +1459,50 @@ | ||
| 1449 | 1459 | return false; |
| 1450 | 1460 | } |
| 1451 | 1461 | |
| 1452 | 1462 | /* |
| 1453 | - * No signing secret stored (webhook subscription not yet registered | |
| 1454 | - * via the Square settings screen). Fall through to the legacy | |
| 1455 | - * merchant_id presence check for backwards compatibility; such sites | |
| 1456 | - * remain unverified until they register webhooks. | |
| 1463 | + * No signing secret stored. Historically the processor fell through | |
| 1464 | + * here to a merchant_id presence check and ACCEPTED the (unsigned) | |
| 1465 | + * event, which let an unauthenticated caller forge Square webhooks on | |
| 1466 | + * any site that had not yet stored a signing secret. Fail closed: the | |
| 1467 | + * event is refused unless enforcement is explicitly disabled. | |
| 1468 | + * | |
| 1469 | + * Sites connected through the Square settings screen store a signing | |
| 1470 | + * secret during onboarding, so they never reach this branch and are | |
| 1471 | + * unaffected. For the rare site that has webhooks enabled but no | |
| 1472 | + * stored secret, refused events stop donations/refunds from | |
| 1473 | + * reconciling, which the hourly Charitable_Square_WebhooksHealthCheck | |
| 1474 | + * detects and surfaces as an admin notice prompting a reconnect (which | |
| 1475 | + * re-registers the subscription and stores a fresh signing secret, | |
| 1476 | + * restoring verification). | |
| 1477 | + * | |
| 1478 | + * @since 1.8.12 | |
| 1457 | 1479 | */ |
| 1458 | - if ( empty( $signature_key ) && charitable_is_debug( 'square' ) ) { | |
| 1459 | - error_log( 'Square webhook WARNING: no signing secret stored - signature verification skipped. Register webhooks via Square settings to enable verification.' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log | |
| 1480 | + if ( empty( $signature_key ) ) { | |
| 1481 | + | |
| 1482 | + /** | |
| 1483 | + * Filter whether to enforce Square webhook signature verification | |
| 1484 | + * when no signing secret is stored. | |
| 1485 | + * | |
| 1486 | + * Defaults to true (fail closed). Returning false restores the | |
| 1487 | + * legacy behaviour of accepting unsigned events and re-opens the | |
| 1488 | + * forgery vector, so it should only be used as a temporary escape | |
| 1489 | + * hatch while reconnecting Square. | |
| 1490 | + * | |
| 1491 | + * @since 1.8.12 | |
| 1492 | + * | |
| 1493 | + * @param bool $enforce Whether to reject unsigned events. Default true. | |
| 1494 | + */ | |
| 1495 | + $enforce_signature = (bool) apply_filters( 'charitable_square_webhook_enforce_signature', true ); | |
| 1496 | + | |
| 1497 | + if ( $enforce_signature ) { | |
| 1498 | + error_log( 'Square webhook REJECTED: no signing secret stored. Reconnect Square (Settings > Payment > Square) to register webhooks and enable signature verification. To temporarily accept unsigned events, use the charitable_square_webhook_enforce_signature filter.' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log | |
| 1499 | + return false; | |
| 1500 | + } | |
| 1501 | + | |
| 1502 | + if ( charitable_is_debug( 'square' ) ) { | |
| 1503 | + error_log( 'Square webhook WARNING: no signing secret stored and signature enforcement disabled via the charitable_square_webhook_enforce_signature filter - accepting unsigned event.' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log | |
| 1504 | + } | |
| 1460 | 1505 | } |
| 1461 | 1506 | |
| 1462 | 1507 | $event = json_decode( $body, true ); |
| 1463 | 1508 | |