Files
kate522 1a9a2762f3 feat(payment): 新增「售后策略」配置与前台售后弹窗链路
- 后台「支付&付费 → 售后策略」:售后功能开关、申请时效、超时效提示(HTML)、
  预置原因、申请售后说明、所需材料、申请说明占位文案、可上传图片开关;
  PHP schema 与 SPA schema 1:1 对齐,group 字段支持拖拽排序
- PaymentRuntime 新增售后设置解析(缺键回退 schema 默认、HTML 走 kses、
  中文原因稳定 slug)与 after_sale_expired 时效判定(支付时间起算)
- 售后工单支持举证图片(order_aftersale.images 列,最多 3 张,
  复用评论图片上传校验)与上传路由;订单视图模型输出 after_sale 按钮状态
- 前台申请 / 超时效提示 / 售后详细三个独立弹窗(modal-mini + 蓝色炫彩头),
  订单详情弹窗不再内嵌售后表单并清理相关死代码
- 新增 after-sale-policy 契约测试与运行时 harness,527 项契约测试通过
2026-10-06 22:21:47 +08:00

118 lines
3.9 KiB
PHP

<?php
/**
* After-sale policy runtime harness.
*
* Covers the 「支付&付费 → 售后策略」 runtime resolution: schema defaults when the
* section was never saved, stored overrides, HTML sanitisation, preset-reason
* slugs, and the after_sale_expired boundary matrix.
*/
namespace {
define('ABSPATH', __DIR__);
define('DAY_IN_SECONDS', 86400);
$GLOBALS['aksrt_test_options'] = [];
$GLOBALS['aksrt_test_now'] = 0;
function sanitize_key($value): string {
return preg_replace('/[^a-z0-9_\-]/', '', strtolower((string) $value)) ?? '';
}
function sanitize_text_field($value): string {
return trim(preg_replace('/[\r\n\t]+/', ' ', strip_tags((string) $value)) ?? '');
}
function sanitize_textarea_field($value): string {
return trim(strip_tags((string) $value));
}
function wp_kses_post($value): string {
$value = preg_replace('#<script\b[^>]*>.*?</script>#is', '', (string) $value);
return strip_tags($value, '<a><b><strong><em><i><span><div><p><br><ul><ol><li>');
}
function esc_url_raw($value): string {
return (string) $value;
}
function wp_json_encode($value): string {
return (string) json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
function get_option(string $name, $default = false) {
return $GLOBALS['aksrt_test_options'][$name] ?? $default;
}
function update_option(string $name, $value): bool {
$GLOBALS['aksrt_test_options'][$name] = $value;
return true;
}
function current_time(string $type, $gmt = false) {
return $GLOBALS['aksrt_test_now'];
}
}
namespace AKSRT\Settings {
class Settings {
public static function get_all(): array {
$stored = get_option('aksrt_settings', []);
return is_array($stored) ? $stored : [];
}
public static function read_domain(string $domain, string $section = ''): array {
return \AKSRT\Admin\SchemaProvider::get_instance()->read_settings($domain, $section);
}
public static function flush_settings_cache(): void {}
}
}
namespace {
require_once dirname(__DIR__, 2) . '/src/Admin/Schema/SchemaPayment.php';
require_once dirname(__DIR__, 2) . '/src/Admin/SchemaProvider.php';
require_once dirname(__DIR__, 2) . '/src/Settings/PaymentRuntime.php';
$GLOBALS['aksrt_test_now'] = strtotime('2026-03-01 12:00:00');
// 1) Section never saved: the runtime still resolves schema defaults.
$defaults = \AKSRT\Settings\PaymentRuntime::get_after_sale_settings();
// 2) Stored values override defaults; HTML fields are kses'ed.
update_option('aksrt_settings', [
'after_sale_s' => false,
'after_sale_time_limit' => 30,
'after_sale_expired_text' => '<div>已超过时效</div><script>alert(1)</script>',
'after_sale_reasons' => [['t' => '质量问题'], ['t' => '发错货']],
'after_sale_desc' => '<div>请保持商品完好</div><script>alert(2)</script>',
'after_sale_materials' => [['t' => '联系邮箱']],
'after_sale_reason_placeholder' => '请描述问题',
'after_sale_img_s' => false,
]);
$overridden = \AKSRT\Settings\PaymentRuntime::get_after_sale_settings();
// 3) Expiry boundary matrix: measured from paid_at, strictly beyond the limit.
$matrix = [];
$boundary_cases = [
'within' => ['2026-02-25 00:00:00', 7, false],
'exact_boundary' => ['2026-02-22 12:00:00', 7, false],
'just_expired' => ['2026-02-22 11:59:59', 7, true],
'long_expired' => ['2026-01-01 00:00:00', 7, true],
'no_limit' => ['2020-01-01 00:00:00', 0, false],
'missing_paid' => ['', 7, false],
];
$stored = get_option('aksrt_settings', []);
foreach ($boundary_cases as $name => $case) {
[$paid_at, $limit, $expected] = $case;
$stored['after_sale_time_limit'] = $limit;
update_option('aksrt_settings', $stored);
$matrix[$name] = \AKSRT\Settings\PaymentRuntime::after_sale_expired($paid_at) === $expected;
}
echo json_encode([
'defaults' => $defaults,
'overridden' => $overridden,
'expired_ok' => $matrix,
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}