Payment API Reference
The Payment API handles all financial transactions on the ZhenRent platform, including task payments, refunds, and balance management.
Overview
ZhenRent uses an escrow-based payment system. When you dispatch a task, funds are held in escrow until task completion. Workers receive payment only after you approve their work.
Payment Flow
1. Dispatch Task → 2. Create Payment → 3. Hold in Escrow → 4. Task Completed → 5. Release to Worker
↓
(Auto-refund if failed)
Key Features:
- Automatic escrow for all tasks
- Instant refunds for failed tasks
- Multiple payment methods (Alipay, WeChat, Cards)
- 20% platform fee on all transactions
- Full transaction history and invoicing
Base URL
https://www.zhenrent.com/api/v1
Authentication
All endpoints require API Key authentication:
Authorization: Bearer zr_live_your_api_key
X-API-Secret: your_api_secret
Supported Payment Methods
Alipay (支付宝) - Recommended for Chinese Users
Fast, secure, and most popular in China.
Features:
- Instant settlement
- QR code or redirect flow
- Widely accepted across China
- Mobile and desktop support
Processing Time: Instant
Refund Time: 1-3 business days
WeChat Pay (微信支付) - Popular Alternative
Integrated with WeChat ecosystem.
Features:
- In-app or QR code payment
- High adoption rate in China
- Mobile-first experience
- Instant confirmation
Processing Time: Instant
Refund Time: 1-3 business days
Credit/Debit Cards - International Support
Standard card processing for global users.
Accepted Cards:
- Visa
- Mastercard
- UnionPay (银联)
- American Express (selected regions)
Processing Time: Instant
Refund Time: 5-10 business days
Bank Transfer - Enterprise Only
Manual processing for high-volume customers.
Requirements:
- Enterprise account verification
- Minimum transfer: 1,000 RMB
- Business license documentation
Processing Time: 1-3 business days
Refund Time: 3-5 business days
Contact: enterprise@zhenrent.com for bank transfer setup.
Create Payment Order
Create a payment order for a task. This endpoint is automatically called when you dispatch a task via the Task API, but you can also call it directly for manual payment flows.
Endpoint
POST /api/v1/payment/create
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| task_id | string | Yes | UUID of the task to pay for |
| amount | integer | Yes | Amount in cents (must match task cost) |
| currency | string | Yes | Currency code: CNY or USD |
| payment_method | string | Yes | Method: alipay, wechat, card, bank_transfer |
| return_url | string | Yes | URL to redirect user after payment |
| callback_url | string | No | Webhook URL for payment notifications |
| metadata | object | No | Custom metadata (max 1KB JSON) |
Example Request
curl -X POST "https://www.zhenrent.com/api/v1/payment/create" \
-H "Authorization: Bearer zr_live_your_api_key" \
-H "X-API-Secret: your_api_secret" \
-H "Content-Type: application/json" \
-d '{
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"amount": 5000,
"currency": "CNY",
"payment_method": "alipay",
"return_url": "https://your-app.com/payment/success",
"callback_url": "https://your-app.com/webhooks/payment"
}'
Python Example
import requests
response = requests.post(
"https://www.zhenrent.com/api/v1/payment/create",
headers={
"Authorization": f"Bearer {API_KEY}",
"X-API-Secret": API_SECRET,
"Content-Type": "application/json"
},
json={
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"amount": 5000,
"currency": "CNY",
"payment_method": "alipay",
"return_url": "https://your-app.com/payment/success",
"callback_url": "https://your-app.com/webhooks/payment"
}
)
payment_data = response.json()
print(f"Redirect user to: {payment_data['payment_url']}")
JavaScript/Node.js Example
const axios = require('axios');
const createPayment = async (taskId, amount) => {
const response = await axios.post(
'https://www.zhenrent.com/api/v1/payment/create',
{
task_id: taskId,
amount: amount,
currency: 'CNY',
payment_method: 'alipay',
return_url: 'https://your-app.com/payment/success',
callback_url: 'https://your-app.com/webhooks/payment'
},
{
headers: {
'Authorization': `Bearer ${process.env.ZHENRENT_API_KEY}`,
'X-API-Secret': process.env.ZHENRENT_API_SECRET,
'Content-Type': 'application/json'
}
}
);
return response.data.payment_url;
};
// Usage
const paymentUrl = await createPayment('550e8400-e29b-41d4-a716-446655440000', 5000);
console.log(`Redirect user to: ${paymentUrl}`);
Success Response (201 Created)
{
"order_id": "ord_abc123def456",
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"payment_url": "https://pay.zhenrent.com/checkout/ord_abc123def456",
"qr_code_url": "https://pay.zhenrent.com/qr/ord_abc123def456.png",
"amount": 5000,
"currency": "CNY",
"payment_method": "alipay",
"status": "pending",
"expires_at": "2026-04-01T11:15:00Z",
"created_at": "2026-04-01T11:00:00Z"
}
Response Fields:
payment_url- Redirect user to this URL to complete paymentqr_code_url- QR code image for mobile payments (Alipay/WeChat)expires_at- Payment link expiration (default: 15 minutes)
Error Responses
| Status Code | Error Code | Description |
|---|---|---|
| 400 | validation_error | Invalid request parameters |
| 400 | amount_mismatch | Amount doesn't match task cost |
| 401 | invalid_credentials | Invalid API key or secret |
| 404 | task_not_found | Task doesn't exist |
| 409 | duplicate_order | Payment order already exists for task |
| 429 | rate_limit_exceeded | Too many requests |
400 Example (Amount Mismatch)
{
"error": {
"code": "amount_mismatch",
"message": "Payment amount (3000 cents) doesn't match task cost (5000 cents)",
"expected_amount": 5000,
"provided_amount": 3000
}
}
Query Payment Status
Retrieve the current status of a payment order.
Endpoint
GET /api/v1/payment/query?order_id={order_id}
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| order_id | string | Yes | Payment order ID |
Example Request
curl "https://www.zhenrent.com/api/v1/payment/query?order_id=ord_abc123def456" \
-H "Authorization: Bearer zr_live_your_api_key" \
-H "X-API-Secret: your_api_secret"
Python Example
def check_payment_status(order_id):
response = requests.get(
f"https://www.zhenrent.com/api/v1/payment/query",
headers={
"Authorization": f"Bearer {API_KEY}",
"X-API-Secret": API_SECRET
},
params={"order_id": order_id}
)
return response.json()
status = check_payment_status("ord_abc123def456")
print(f"Payment status: {status['status']}")
Success Response (200 OK)
{
"order_id": "ord_abc123def456",
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"amount": 5000,
"currency": "CNY",
"payment_method": "alipay",
"status": "completed",
"transaction_id": "txn_xyz789",
"paid_at": "2026-04-01T11:05:23Z",
"created_at": "2026-04-01T11:00:00Z",
"cost_breakdown": {
"worker_payout_cents": 4000,
"platform_fee_cents": 1000,
"total_cents": 5000
}
}
Payment Status Values:
pending- Payment created, awaiting user actionprocessing- Payment submitted, processingcompleted- Payment successful, funds in escrowfailed- Payment failedexpired- Payment link expired (15 min timeout)refunded- Payment refunded to user
Error Responses
| Status Code | Error Code | Description |
|---|---|---|
| 401 | invalid_credentials | Invalid authentication |
| 404 | order_not_found | Order doesn't exist |
Request Refund
Request a refund for a completed payment. Refunds are automatically processed for failed or cancelled tasks, but you can also request manual refunds.
Endpoint
POST /api/v1/payment/refund
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| order_id | string | Yes | Payment order ID to refund |
| amount | integer | No | Partial refund amount in cents (default: full) |
| reason | string | Yes | Refund reason (max 500 chars) |
| refund_method | string | No | Method: original (default), balance |
Refund Reasons (Required):
task_cancelled- Task was cancelled before completionquality_issue- Work quality doesn't meet requirementstask_failed- Worker failed to complete taskduplicate_payment- Accidental duplicate paymentother- Other reason (provide details)
Example Request
curl -X POST "https://www.zhenrent.com/api/v1/payment/refund" \
-H "Authorization: Bearer zr_live_your_api_key" \
-H "X-API-Secret: your_api_secret" \
-H "Content-Type: application/json" \
-d '{
"order_id": "ord_abc123def456",
"reason": "task_cancelled",
"refund_method": "original"
}'
Python Example
def request_refund(order_id, reason):
response = requests.post(
"https://www.zhenrent.com/api/v1/payment/refund",
headers={
"Authorization": f"Bearer {API_KEY}",
"X-API-Secret": API_SECRET,
"Content-Type": "application/json"
},
json={
"order_id": order_id,
"reason": reason,
"refund_method": "original"
}
)
return response.json()
refund = request_refund("ord_abc123def456", "task_cancelled")
print(f"Refund ID: {refund['refund_id']}")
print(f"Expected in: {refund['estimated_arrival']}")
Success Response (200 OK)
{
"refund_id": "ref_xyz789",
"order_id": "ord_abc123def456",
"amount": 5000,
"currency": "CNY",
"status": "processing",
"refund_method": "alipay",
"estimated_arrival": "2026-04-03T11:00:00Z",
"created_at": "2026-04-01T11:30:00Z"
}
Refund Processing Times:
- Alipay: 1-3 business days
- WeChat Pay: 1-3 business days
- Credit/Debit cards: 5-10 business days
- Bank transfer: 3-5 business days
- Account balance: Instant
Error Responses
| Status Code | Error Code | Description |
|---|---|---|
| 400 | refund_not_allowed | Refund not permitted for this order |
| 400 | already_refunded | Order already refunded |
| 404 | order_not_found | Order doesn't exist |
400 Example (Already Refunded)
{
"error": {
"code": "already_refunded",
"message": "Order has already been fully refunded",
"refund_id": "ref_xyz789",
"refunded_at": "2026-04-01T11:30:00Z"
}
}
Check Account Balance
Query your current account balance and transaction summary.
Endpoint
GET /api/v1/payment/balance
Example Request
curl "https://www.zhenrent.com/api/v1/payment/balance" \
-H "Authorization: Bearer zr_live_your_api_key"
Python Example
def get_balance():
response = requests.get(
"https://www.zhenrent.com/api/v1/payment/balance",
headers={"Authorization": f"Bearer {API_KEY}"}
)
return response.json()
balance = get_balance()
print(f"Available balance: ¥{balance['balance_cents']/100:.2f}")
print(f"Pending in escrow: ¥{balance['escrow_cents']/100:.2f}")
Success Response (200 OK)
{
"balance_cents": 50000,
"balance_rmb": 500.00,
"currency": "CNY",
"escrow_cents": 15000,
"escrow_rmb": 150.00,
"lifetime_spent_cents": 200000,
"lifetime_spent_rmb": 2000.00,
"last_updated": "2026-04-01T11:00:00Z"
}
Balance Fields:
balance_cents- Available balance you can spendescrow_cents- Funds held in escrow for active taskslifetime_spent_cents- Total amount spent since account creation
Get Transaction History
Retrieve paginated list of all payment transactions.
Endpoint
GET /api/v1/payment/transactions
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| type | string | all | Filter: payment, refund, deposit, all |
| status | string | all | Filter: completed, pending, failed, all |
| limit | integer | 20 | Results per page (max: 100) |
| starting_after | string | - | Cursor for pagination |
Example Request
curl "https://www.zhenrent.com/api/v1/payment/transactions?type=payment&limit=10" \
-H "Authorization: Bearer zr_live_your_api_key"
Python Example
def get_transactions(transaction_type='all', limit=20):
response = requests.get(
"https://www.zhenrent.com/api/v1/payment/transactions",
headers={"Authorization": f"Bearer {API_KEY}"},
params={"type": transaction_type, "limit": limit}
)
return response.json()
transactions = get_transactions(transaction_type='payment')
for tx in transactions['data']:
print(f"{tx['created_at']}: ¥{tx['amount']/100} - {tx['description']}")
Success Response (200 OK)
{
"data": [
{
"transaction_id": "txn_abc123",
"type": "payment",
"order_id": "ord_abc123def456",
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"amount": 5000,
"currency": "CNY",
"status": "completed",
"description": "Payment for task: Verify restaurant opening hours",
"created_at": "2026-04-01T11:05:23Z"
},
{
"transaction_id": "txn_def456",
"type": "refund",
"order_id": "ord_xyz789",
"amount": 3000,
"currency": "CNY",
"status": "completed",
"description": "Refund for cancelled task",
"created_at": "2026-03-30T14:20:00Z"
}
],
"has_more": true,
"next_cursor": "txn_def456"
}
Payment Webhooks
Configure webhook endpoints to receive real-time payment notifications.
Webhook Events
ZhenRent sends webhook events for the following payment events:
payment.created- Payment order createdpayment.completed- Payment successfulpayment.failed- Payment failedpayment.expired- Payment link expiredrefund.created- Refund initiatedrefund.completed- Refund processedrefund.failed- Refund failed
Webhook Payload
All webhook payloads include these fields:
{
"event": "payment.completed",
"timestamp": "2026-04-01T11:05:23Z",
"data": {
"order_id": "ord_abc123def456",
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"amount": 5000,
"currency": "CNY",
"payment_method": "alipay",
"status": "completed",
"transaction_id": "txn_xyz789"
},
"signature": "sha256=abc123def456..."
}
Verifying Webhook Signatures
Always verify webhook signatures to ensure authenticity.
Python Example
import hmac
import hashlib
def verify_webhook_signature(payload, signature, secret):
"""
Verify ZhenRent webhook signature.
Args:
payload: Raw request body (bytes)
signature: X-Signature header value
secret: Your webhook secret from dashboard
Returns:
bool: True if signature is valid
"""
expected_signature = hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
# Extract signature from header (format: "sha256=...")
provided_signature = signature.split('=')[1] if '=' in signature else signature
return hmac.compare_digest(expected_signature, provided_signature)
# Flask example
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/webhooks/payment', methods=['POST'])
def payment_webhook():
# Get signature from header
signature = request.headers.get('X-Signature')
if not signature:
return jsonify({'error': 'Missing signature'}), 401
# Verify signature
if not verify_webhook_signature(request.data, signature, WEBHOOK_SECRET):
return jsonify({'error': 'Invalid signature'}), 401
# Process webhook
payload = request.json
event = payload['event']
if event == 'payment.completed':
order_id = payload['data']['order_id']
print(f"Payment completed: {order_id}")
# Update your database, send confirmation email, etc.
elif event == 'payment.failed':
order_id = payload['data']['order_id']
print(f"Payment failed: {order_id}")
# Notify user, retry payment, etc.
return jsonify({'status': 'received'}), 200
Node.js Example
const crypto = require('crypto');
const express = require('express');
const app = express();
// Important: Use raw body for signature verification
app.use('/webhooks/payment', express.raw({ type: 'application/json' }));
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
const providedSignature = signature.split('=')[1] || signature;
return crypto.timingSafeEqual(
Buffer.from(expectedSignature),
Buffer.from(providedSignature)
);
}
app.post('/webhooks/payment', (req, res) => {
const signature = req.headers['x-signature'];
if (!signature) {
return res.status(401).json({ error: 'Missing signature' });
}
if (!verifyWebhookSignature(req.body, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const payload = JSON.parse(req.body);
const { event, data } = payload;
switch (event) {
case 'payment.completed':
console.log(`Payment completed: ${data.order_id}`);
// Handle successful payment
break;
case 'payment.failed':
console.log(`Payment failed: ${data.order_id}`);
// Handle failed payment
break;
default:
console.log(`Unhandled event: ${event}`);
}
res.json({ status: 'received' });
});
Webhook Best Practices
- Always verify signatures - Never process unverified webhooks
- Use HTTPS endpoints - Webhook URLs must use HTTPS
- Respond quickly - Return 200 OK within 5 seconds
- Handle idempotency - Same webhook may be sent multiple times
- Store webhook IDs - Use
event_idto prevent duplicate processing
Idempotency Example
# Store processed webhook IDs in database
processed_webhooks = set() # In production, use Redis or database
@app.route('/webhooks/payment', methods=['POST'])
def payment_webhook():
payload = request.json
event_id = payload.get('event_id')
# Check if already processed
if event_id in processed_webhooks:
return jsonify({'status': 'already_processed'}), 200
# Process webhook
process_payment_event(payload)
# Mark as processed
processed_webhooks.add(event_id)
return jsonify({'status': 'received'}), 200
Error Codes Reference
Common payment-related error codes:
| Error Code | Description | Resolution |
|---|---|---|
PAYMENT_FAILED | Payment processing failed | User should retry with different method |
INSUFFICIENT_FUNDS | User account has insufficient balance | User should add funds to account |
PAYMENT_TIMEOUT | Payment expired (15 min default) | Create new payment order |
DUPLICATE_ORDER | Order already exists for task | Query existing order status |
INVALID_SIGNATURE | Webhook signature verification failed | Check webhook secret configuration |
REFUND_NOT_ALLOWED | Refund not permitted | Check refund policy and task status |
AMOUNT_MISMATCH | Payment amount doesn't match task cost | Use correct amount from task details |
PAYMENT_METHOD_UNAVAILABLE | Selected payment method not available | Choose different payment method |
Error Response Format
{
"error": {
"code": "PAYMENT_FAILED",
"message": "Payment processing failed due to insufficient funds",
"details": {
"provider_code": "NSF",
"provider_message": "Insufficient balance"
},
"retry_allowed": true,
"suggested_actions": [
"Add funds to your account",
"Try a different payment method"
]
}
}
Security Best Practices
1. Protect API Credentials
Never expose API keys or secrets in client-side code.
# Bad - Hardcoded credentials
API_KEY = "zr_live_abc123..."
API_SECRET = "secret_xyz..."
# Good - Environment variables
import os
API_KEY = os.getenv("ZHENRENT_API_KEY")
API_SECRET = os.getenv("ZHENRENT_API_SECRET")
2. Use HTTPS Only
All payment endpoints require HTTPS. HTTP requests will be rejected.
# Bad
url = "http://www.zhenrent.com/api/v1/payment/create"
# Good
url = "https://www.zhenrent.com/api/v1/payment/create"
3. Verify All Webhooks
Always verify webhook signatures before processing.
# Bad - Processing unverified webhook
@app.route('/webhooks/payment', methods=['POST'])
def webhook():
payload = request.json
process_payment(payload) # DANGEROUS!
# Good - Verify signature first
@app.route('/webhooks/payment', methods=['POST'])
def webhook():
if not verify_signature(request.data, request.headers['X-Signature']):
return jsonify({'error': 'Invalid signature'}), 401
payload = request.json
process_payment(payload)
4. Implement Rate Limiting
Protect your webhook endpoints from abuse.
from flask_limiter import Limiter
limiter = Limiter(app, key_func=lambda: request.remote_addr)
@app.route('/webhooks/payment', methods=['POST'])
@limiter.limit("100 per minute")
def webhook():
# Process webhook
pass
5. Store Sensitive Data Securely
Never log or store full payment details.
# Bad - Logging sensitive data
logger.info(f"Processing payment: {payment_data}")
# Good - Log only non-sensitive identifiers
logger.info(f"Processing payment order: {order_id}")
6. Handle Errors Gracefully
Don't expose internal error details to users.
try:
response = create_payment(task_id, amount)
except Exception as e:
# Bad - Exposing internal error
return jsonify({'error': str(e)}), 500
# Good - Generic error message
logger.error(f"Payment creation failed: {e}")
return jsonify({
'error': 'Payment processing failed. Please try again.'
}), 500
Testing Payment Integration
Sandbox Environment
Use the sandbox environment for testing without real money.
Sandbox Base URL:
https://sandbox.zhenrent.com/api/v1
Test API Credentials:
- API Key:
zr_test_sandbox_key_123 - API Secret:
test_secret_456
Test Payment Methods:
All test payments automatically succeed after 5 seconds:
# Test with Alipay
payment = create_payment(
task_id="test_task_123",
amount=5000,
payment_method="alipay"
)
# Payment will auto-complete in 5 seconds
Test Card Numbers:
| Card Number | Result |
|---|---|
| 4242 4242 4242 4242 | Success |
| 4000 0000 0000 0002 | Declined |
| 4000 0000 0000 9995 | Insufficient funds |
Test Scenarios:
# Test successful payment
def test_successful_payment():
payment = create_payment(task_id, 5000, "alipay")
assert payment['status'] == 'pending'
# Wait for auto-complete (sandbox only)
time.sleep(6)
status = query_payment(payment['order_id'])
assert status['status'] == 'completed'
# Test failed payment
def test_failed_payment():
payment = create_payment(
task_id,
5000,
"card",
test_card="4000000000000002"
)
# Will fail automatically in sandbox
# Test refund
def test_refund():
payment = create_payment(task_id, 5000, "alipay")
time.sleep(6) # Wait for completion
refund = request_refund(payment['order_id'], "task_cancelled")
assert refund['status'] == 'processing'
Webhook Testing
Use webhook.site or ngrok for local testing:
# Start ngrok tunnel
ngrok http 5000
# Use ngrok URL as callback_url
curl -X POST "https://sandbox.zhenrent.com/api/v1/payment/create" \
-H "Authorization: Bearer zr_test_sandbox_key_123" \
-d '{
"task_id": "test_task_123",
"amount": 5000,
"payment_method": "alipay",
"callback_url": "https://abc123.ngrok.io/webhooks/payment"
}'
Frequently Asked Questions
When is payment charged?
Payment is charged when you dispatch a task via the Task API. The POST /api/v1/tasks endpoint automatically creates a payment order and holds funds in escrow.
What happens to payment if task fails?
Automatic full refund within 1-3 business days. No action required on your part.
Can I get an invoice?
Yes. Invoices are automatically generated for all payments and available in your dashboard at https://www.zhenrent.com/dev/dashboard/invoices.
What is the payment timeout?
Payment links expire after 15 minutes by default. You can configure this when creating the payment order (minimum: 5 minutes, maximum: 24 hours).
payment = create_payment(
task_id="...",
amount=5000,
payment_method="alipay",
expires_in_seconds=1800 # 30 minutes
)
How long until refunds are processed?
Processing times vary by payment method:
- Alipay/WeChat: 1-3 business days
- Credit cards: 5-10 business days
- Bank transfer: 3-5 business days
- Account balance: Instant
Can I dispute a payment?
Yes. Contact support@zhenrent.com with your order ID and reason. Response time: under 24 hours.
What currencies are supported?
Currently supported:
- CNY (Chinese Yuan) - Primary
- USD (US Dollar) - Converted to CNY at current exchange rate
Are there transaction limits?
Per Transaction:
- Minimum: 10 RMB (1,000 cents)
- Maximum: 50,000 RMB (5,000,000 cents)
Daily Limits:
- Standard accounts: 100,000 RMB/day
- Verified accounts: 500,000 RMB/day
- Enterprise accounts: No limit
Contact enterprise@zhenrent.com to increase limits.
How do I add funds to my account?
Visit your dashboard at https://www.zhenrent.com/dev/dashboard/wallet and click "Recharge". Minimum deposit: 100 RMB.
Integration Checklist
Before going live, ensure you've completed:
- Tested all payment flows in sandbox environment
- Implemented webhook signature verification
- Set up error handling for all payment endpoints
- Configured webhook endpoint with HTTPS
- Tested refund scenarios
- Implemented idempotency for webhooks
- Stored API credentials securely (environment variables)
- Added logging for payment transactions
- Tested payment timeout scenarios
- Reviewed and understood all error codes
- Set up monitoring for failed payments
- Tested with all supported payment methods
Related Documentation
- Task API - Create and manage tasks
- Webhooks - Real-time event notifications
- Error Codes - Complete error reference
- Pricing Guide - Understand platform fees and costs
- Quick Start - Get started in 15 minutes
Support
Payment Issues: support@zhenrent.com
Enterprise Sales: enterprise@zhenrent.com
Response Time: Under 24 hours
Documentation Feedback: Found an issue or have a suggestion? Email docs@zhenrent.com