# API 使用文档

## 概述

本 API 提供了一个通用的应用撤回接口，支持处理不同国家的各种应用撤回请求。通过提供业务记录标识、推送场景、推送类型和国家代码，系统将自动路由到相应的处理逻辑，在状态校验通过后执行撤回操作。

## 接口信息

- **接口地址**: `/index.php`
- **请求方法**: `POST`
- **Content-Type**: `application/x-www-form-urlencoded` 或 `application/json`
- **响应格式**: `application/json`

## 请求参数

所有参数通过 POST 方法提交：

| 参数名 | 类型 | 必需 | 说明 | 示例 |
|--------|------|------|------|------|
| BusinessId | string | 是 | 业务记录 ID | `abc-123-def` |
| BusinessSerialNumber | string | 是 | 业务流水号 | `POEPR20260407000161` |
| PushId | string | 是 | 推送记录 ID | `push-001` |
| PushScene | string | 是 | 推送场景（字母），与方法名的场景部分对应 | `vatpushtax`, `vatpushdec`, `eprpushreg` |
| PushType | string | 是 | 推送类型（数字），与方法名的类型部分对应 | `101`, `201`, `301` |
| PushTaxBureauStatus | string | 否 | 推送税局状态 | `1` |
| Country | string | 是 | 国家代码（字母） | `DE`, `FR`, `IT` |

### 应用名称的构造规则

系统会自动将 `PushScene` 和 `PushType` 拼接为方法名，规则为：

```
{小写 PushScene}_{小写 PushType}
```

例如：
- `PushScene=vatpushtax`, `PushType=101` → 调用 `vatpushtax_101()` 方法
- `PushScene=vatpushdec`, `PushType=201` → 调用 `vatpushdec_201()` 方法
- `PushScene=eprpushreg`, `PushType=301` → 调用 `eprpushreg_301()` 方法

## 状态校验规则

在执行撤回操作前，系统会查询数据库记录的当前 `status` 字段，**仅当 status 为以下值时允许撤回**：

| 国家 | 允许撤回的 status 值 |
|------|---------------------|
| 德国 (DE) | 0（待处理）、1（处理中） |
| 法国 (FR) | 0（待处理）、-1（待处理） |
| 意大利 (IT) | 0（待处理）、-1（待处理） |
| 奥地利 (AT) | 0（待处理）、-1（待处理） |
| 英国 (GB) | 0、1、3（registration_status） |

若当前状态不允许撤回，将返回 `code=400` 及相应的错误提示。

## 响应格式

### 成功响应

```json
{
    "code": 200,
    "msg": "德国VAT注册撤回成功",
    "data": {
        "tid": "POEPR20260407000161",
        "affected_rows": 1,
        "status": "revoked"
    }
}
```

### 失败响应

```json
{
    "code": 400,
    "msg": "错误描述信息",
    "data": null
}
```

### 响应字段说明

| 字段 | 类型 | 说明 |
|------|------|------|
| code | int | 状态码，200 表示成功，400 表示失败 |
| msg | string | 操作结果描述或错误信息 |
| data | mixed | 返回数据（成功时可能包含 tid、affected_rows 等） |

## 错误场景

### 1. 请求方法错误

**请求**: 使用 GET 方法访问

**响应**:
```json
{
    "code": 400,
    "msg": "仅支持 POST 请求",
    "data": null
}
```

### 2. 缺少必需参数

**请求**: 缺少 `Country` 参数

**响应**:
```json
{
    "code": 400,
    "msg": "缺少必需参数：country",
    "data": null
}
```

### 3. 不支持的国家

**请求**: `Country=XX`（不存在的国家代码）

**响应**:
```json
{
    "code": 400,
    "msg": "不支持的国家：XX",
    "data": null
}
```

### 4. 不支持的应用

**请求**: `PushScene=unknown`, `PushType=999`（国家类中不存在的方法）

**响应**:
```json
{
    "code": 400,
    "msg": "不支持的应用：unknown_999",
    "data": null
}
```

### 5. 记录状态不允许撤回

**请求**: 记录已处理完成（status=2），无法撤回

**响应**:
```json
{
    "code": 400,
    "msg": "该记录当前状态不允许撤回",
    "data": {
        "tid": "POEPR20260407000161",
        "current_status": 2
    }
}
```

### 6. 未找到记录

**请求**: 提供的流水号在数据库中不存在

**响应**:
```json
{
    "code": 400,
    "msg": "未找到指定的德国VAT注册记录",
    "data": {
        "tid": "POEPR20260407000161"
    }
}
```

## 使用示例

### cURL 请求示例

#### 德国 VAT 注册撤回

```bash
curl -X POST http://your-domain.com/app_withdrawn/index.php \
  -d "BusinessId=abc-123" \
  -d "BusinessSerialNumber=POEPR20260407000161" \
  -d "PushId=push-001" \
  -d "PushScene=vatpushtax" \
  -d "PushType=101" \
  -d "PushTaxBureauStatus=1" \
  -d "Country=DE"
```

#### 法国 VAT 注册撤回

```bash
curl -X POST http://your-domain.com/app_withdrawn/index.php \
  -d "BusinessId=abc-456" \
  -d "BusinessSerialNumber=FR20260407000161" \
  -d "PushId=push-002" \
  -d "PushScene=vatpushtax" \
  -d "PushType=101" \
  -d "PushTaxBureauStatus=1" \
  -d "Country=FR"
```

#### 意大利 VAT AA7 注册撤回

```bash
curl -X POST http://your-domain.com/app_withdrawn/index.php \
  -d "BusinessId=abc-789" \
  -d "BusinessSerialNumber=IT20260407000161" \
  -d "PushId=push-003" \
  -d "PushScene=vatpushtax" \
  -d "PushType=109" \
  -d "PushTaxBureauStatus=1" \
  -d "Country=IT"
```

### PHP 客户端示例

```php
<?php

$url = 'http://your-domain.com/app_withdrawn/index.php';
$data = [
    'BusinessId' => 'abc-123',
    'BusinessSerialNumber' => 'POEPR20260407000161',
    'PushId' => 'push-001',
    'PushScene' => 'vatpushtax',
    'PushType' => '101',
    'PushTaxBureauStatus' => '1',
    'Country' => 'DE'
];

$options = [
    'http' => [
        'header'  => "Content-type: application/x-www-form-urlencoded\r\n",
        'method'  => 'POST',
        'content' => http_build_query($data)
    ]
];

$context  = stream_context_create($options);
$result = file_get_contents($url, false, $context);
$response = json_decode($result, true);

if ($response['code'] === 200) {
    echo "撤回成功: " . $response['msg'];
} else {
    echo "撤回失败: " . $response['msg'];
}
```

### JavaScript (Fetch API) 示例

```javascript
const url = 'http://your-domain.com/app_withdrawn/index.php';
const data = new URLSearchParams({
    BusinessId: 'abc-123',
    BusinessSerialNumber: 'POEPR20260407000161',
    PushId: 'push-001',
    PushScene: 'vatpushtax',
    PushType: '101',
    PushTaxBureauStatus: '1',
    Country: 'DE'
});

fetch(url, {
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: data
})
.then(response => response.json())
.then(result => {
    if (result.code === 200) {
        console.log('撤回成功:', result.msg);
    } else {
        console.error('撤回失败:', result.msg);
    }
})
.catch(error => {
    console.error('请求错误:', error);
});
```

### Python 请求示例

```python
import requests

url = 'http://your-domain.com/app_withdrawn/index.php'
data = {
    'BusinessId': 'abc-123',
    'BusinessSerialNumber': 'POEPR20260407000161',
    'PushId': 'push-001',
    'PushScene': 'vatpushtax',
    'PushType': '101',
    'PushTaxBureauStatus': '1',
    'Country': 'DE'
}

response = requests.post(url, data=data)
result = response.json()

if result['code'] == 200:
    print(f"撤回成功: {result['msg']}")
else:
    print(f"撤回失败: {result['msg']}")
```

## 支持的国家和应用

### 德国 (DE)

| 应用名称 | 说明 | 数据库表 | 匹配字段 |
|---------|------|---------|---------|
| vatpushtax_101 | VAT 注册撤回 | vat_de_register | order_serial_number |
| vatpushdec_201 | VAT 普通申报撤回 | tax_declaration | tid |
| vatpushdec_202 | VAT B2B 申报撤回 | tax_declaration | tid + declare_type='zmdo' |
| vatpushdec_203 | VAT 延缓申报撤回 | vat_de_application_delay | tid |
| eprpushreg_301 | EPR 申请注册码撤回 | pack_de_register | tid + pack_type=1 |
| eprpushreg_302 | EPR 注销注册码撤回 | pack_de_register | tid + pack_type=2 |

### 法国 (FR)

| 应用名称 | 说明 | 数据库表 | 匹配字段 |
|---------|------|---------|---------|
| vatpushtax_101 | VAT 注册撤回 | vat_fr_register | tid |
| vatpushtax_105 | EORI 注册撤回 | eori_fr_register | tid |

### 意大利 (IT)

| 应用名称 | 说明 | 数据库表 | 匹配字段 |
|---------|------|---------|---------|
| vatpushtax_109 | VAT AA7 注册撤回 | it_profis_register | tid |
| vatpushtax_110 | VAT ANR3 注册撤回 | anr3_it_profis_register | tid |

### 英国 (GB)

| 应用名称 | 说明 | 数据库表 | 匹配字段 |
|---------|------|---------|---------|
| vatpushtax_101 | VAT 注册撤回 | uk_vat_register | source_record_id（用 PushId 匹配） |

> **注意**：英国 (GB) 撤回规则与 DE/FR 不同——允许 registration_status 为 0/1/3 时撤回，置 registration_status=4；其中 status=1 时同时置 registration_failure_count=10。

### 比利时 (BE)

| 应用名称 | 说明 | 数据库表 | 匹配字段 |
|---------|------|---------|---------|
| eprpushreg_301 | EPR 申请注册码撤回 | pack_be_register | tid |

### 奥地利 (AT)

| 应用名称 | 说明 | 数据库表 | 匹配字段 |
|---------|------|---------|---------|
| eprpushreg_301 | EPR 申请注册码撤回 | pack_be_register | tid |

## 注意事项

1. **参数验证**: 所有必需参数都必须提供且不能为空字符串
2. **国家代码**: 不区分大小写，系统会自动转为小写处理，如 `DE`、`de` 均可
3. **状态限制**: 只有状态为"待处理"或"处理中"的记录才能撤回，已完成的记录无法撤回
4. **幂等性**: 重复提交相同的撤回请求可能会返回"未找到匹配的记录"错误
5. **安全性**: 建议在生产环境中添加 API 密钥验证或其他身份认证机制

## 常见问题

### Q: 如何知道支持哪些国家和应用？

A: 参见上方的"支持的国家和应用"表格，或查看 `countries/` 目录下的 PHP 文件。

### Q: 撤回操作可以撤销吗？

A: 撤回操作会直接修改数据库记录状态，如需恢复需要通过其他方式手动处理。

### Q: 为什么我的请求返回"该记录当前状态不允许撤回"？

A: 该记录已经完成处理（status 已不为 0 或 -1），无法再撤回。只有待处理或处理中的记录才能撤回。

### Q: 国家代码是否区分大小写？

A: 不区分。`DE`、`De`、`de` 都会被识别为德国。

## 技术支持

如有问题或需要添加新的国家/应用支持，请联系开发团队。
