本地Odoo环境下Ajax调用Midtrans POST API跨域权限错误排查
问题分析与解决方案
为什么会出现OPTIONS预请求?
当你发起跨域POST请求,且满足以下任一条件时,浏览器会自动发送OPTIONS预请求(预检请求)来验证服务器是否允许该实际请求:
- 请求包含自定义HTTP头(比如你这里的
Authorization) - 请求的
Content-Type是application/json(非application/x-www-form-urlencoded、multipart/form-data或text/plain)
所以即使你设置了POST方法,浏览器依然会先发OPTIONS请求确认权限,这是跨域请求的标准机制。
核心错误原因
你设置了xhrFields: { withCredentials: true },这个属性会告诉浏览器在跨域请求中携带当前域名的Cookie等凭证。但此时CORS响应头Access-Control-Allow-Origin不能是通配符*,必须是具体的请求源(比如你的http://10.0.75.1:8069)。而cors-anywhere默认返回的Access-Control-Allow-Origin是*,这就导致了冲突,触发了报错。
更关键的是:调用Midtrans Snap API完全不需要携带你本地Odoo的凭证,这个withCredentials设置是多余的!
修复步骤与修改后的代码
- 移除不必要的
withCredentials配置:这是解决CORS错误的核心。 - 清理冗余代码:你同时初始化了原生
XMLHttpRequest和jQuery的$.ajax,保留后者即可。 - 用JSON对象代替字符串格式的
data:避免手动转义的错误,让jQuery自动处理JSON序列化。 - 统一请求配置:
type和method重复了,保留一个即可。
修改后的代码如下:
function make_base_auth(user, password) { var tok = user; var hash = btoa(tok); return "Basic " + hash; } $.ajax({ url: "https://cors-anywhere.herokuapp.com/https://app.sandbox.midtrans.com/snap/v1/transactions", method: 'POST', dataType: "json", crossDomain: true, async: true, // 建议设为true,同步请求会阻塞浏览器 contentType: "application/json; charset=utf-8", beforeSend: function(xhr){ xhr.setRequestHeader('Authorization', make_base_auth("SB-Mid-server-aaaa:","")); }, // 用JSON对象代替字符串,更易维护 data: JSON.stringify({ "transaction_details": { "order_id": "ORDER-109", "gross_amount": 75000 }, "credit_card": { "secure": true }, "item_details": [ { "id": "ITEM1", "price": 10000, "quantity": 5, "name": "Midtrans wwwwww" }, { "id": "ITEM2", "price": 5000, "quantity": 5, "name": "Midtrans wwdwdwdwd" } ], "customer_details": { "first_name": "TEST", "last_name": "MIDTRANSER", "email": "noreply@example.com", "phone": "+628123456", "billing_address": { "first_name": "TEST", "last_name": "MIDTRANSER", "email": "noreply@example.com", "phone": "081 2233 44-55", "address": "Sudirman", "city": "Jakarta", "postal_code": "12190", "country_code": "IDN" }, "shipping_address": { "first_name": "TEST", "last_name": "MIDTRANSER", "email": "noreply@example.com", "phone": "0812345678910", "address": "Sudirman", "city": "Jakarta", "postal_code": "12190", "country_code": "IDN" } } }), success: function (data) { console.log(data); snap.pay(data.token); alert("请求成功"); }, error: function (xhr, status, error) { console.error("请求失败:", xhr.responseText); alert("请求失败,请查看控制台日志"); } });
额外建议
- 避免使用同步请求:把
async: false改成true,同步请求会阻塞浏览器主线程,影响用户体验。 - 自行部署CORS代理:免费的
cors-anywhere.herokuapp.com有请求限制,长期使用建议自己部署一个CORS代理服务(比如基于Node.js的cors-anywhere包)。 - 后端中转请求(更优方案):在生产环境中,不建议在前端直接调用Midtrans API(会暴露你的Server Key)。正确的做法是在Odoo后端编写一个控制器,由后端中转请求到Midtrans API,前端调用Odoo的控制器接口,这样完全避免跨域问题,也更安全。
内容的提问来源于stack exchange,提问作者Albert Chang
相关产品推荐
相关产品推荐

