*** 模組資料夾 payment_sinopac 以 "/" 來代表此資料夾 ***
payment_acquirer_data.xml
,有注意到 data 標籤有個 noupdate="1"
嗎? 代表只有安裝模組的時候才會寫入,升級模組不會/data/payment_acquirer_data.xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record id="payment_acquirer_sinopac" model="payment.acquirer">
<field name="name">永豐金流支付</field>
<field name="provider">sinopac</field>
<field name="company_id" ref="base.main_company"/>
<field name="view_template_id" ref="sinopac_form"/>
<field name="shop_no">ShopNo</field>
<field name="sinopac_a1">0123456789ABCDEF</field>
<field name="sinopac_a2">0123456789ABCDEF</field>
<field name="sinopac_b1">0123456789ABCDEF</field>
<field name="sinopac_b2">0123456789ABCDEF</field>
</record>
</data>
</odoo>
payment.acquirer
,這裡都還沒處理好,目前還卡在建立訂單的流程,不過還是先附上 code/models/payment_acquirer.py
# -*- coding: utf-8 -*-
from werkzeug import urls
from datetime import datetime, timedelta
from odoo import models, fields, api
from odoo.http import request
from odoo.exceptions import UserError
class PaymentAcquirer(models.Model):
_inherit = 'payment.acquirer'
provider = fields.Selection(
selection_add=[('sinopac', '永豐金流')],
ondelete={'sinopac': 'set default'})
shop_no = fields.Char(string='商店代號')
sinopac_a1 = fields.Char(string='Key A1')
sinopac_a2 = fields.Char(string='Key A2')
sinopac_b1 = fields.Char(string='Key B1')
sinopac_b2 = fields.Char(string='Hash Key B2')
domain = fields.Char(string='對外連結域名')
def sinopac_form_generate_values(self, values):
sale_order_name = values['reference'].split('-', 1)[0]
sdk = self.env['payment.transaction']._sinopac_get_sdk()
today = datetime.now()
# 取得 domain
# base_url = base_url.replace('http:', 'https:', 1)
base_url = self.sinopac_domain if self.sinopac_domain else self.env['ir.config_parameter'].sudo().get_param('web.base.url')
amount = values["amount"].__round__()
order_sinopac_data = {
'order_no': sale_order_name,
'prdt_name': 'Odoo 電商消費',
'amount': amount,
'pay_type': '',
}
data = {
'ShopNo': sdk.shop_no,
'OrderNo': sale_order_name,
'Amount': f'{amount}00',
'CurrencyID': 'TWD',
'PrdtName': 'Odoo 電商消費',
'ReturnURL': f'{base_url}/payment/sinopac/payment',
'BackendURL': f'{base_url}/payment/sinopac/receive_msg',
'PayType': 'A',
'ATMParam': {
'ExpireDate': (today + timedelta(days=7)).strftime('%Y%m%d')
}
}
pay_type = request.session.get('payment', 'undefined')
if pay_type == 'C':
data.update({
'PayType': pay_type,
'CardParam': {
'AutoBilling': 'Y'
}
})
order_sinopac_data.update({
'pay_type': pay_type,
'auto_billing': 'Y',
})
elif pay_type == 'A':
after_7_day = today + timedelta(days=7)
data.update({
'PayType': pay_type,
'ATMParam': {
'ExpireDate': after_7_day.strftime('%Y%m%d')
}
})
order_sinopac_data.update({
'pay_type': pay_type,
'expire_date': after_7_day
})
else:
raise UserError('不存在的永豐金流交易類型 %s' % pay_type)
# TODO 這裡要測寫進 order.sinopac 後回傳畫面看是否正常
# reply = sdk.call_api(
# url='https://apisbx.sinopac.com/funBIZ/QPay.WebAPI/api/Order',
# data=self.request_dataset('OrderCreate', data)
# )
return order_sinopac_data
@api.model
def _get_sinopac_urls(self, environment):
if environment == 'enabled':
return {
# 正式環境
'sinopac_form_url': 'http://localhost',
}
else:
return {
# 測試環境
'sinopac_form_url': 'https://payment-stage.sinopac.com.tw/Cashier/AioCheckOut/V5',
}
def sinopac_get_form_action_url(self):
return self._get_sinopac_urls(self.state)['sinopac_form_url']
payment.transaction
,註冊方法,這裡還在理解怎麼丟(看著Paypal跟綠界的模組混亂中)/models/payment_transaction.py
import logging
import pprint
from odoo import api, fields, models, _
from odoo.addons.payment.models.payment_acquirer import ValidationError
from odoo.addons.payment_sinopac.controller.sinopac_sdk import SinopacSDK
_logger = logging.getLogger(__name__)
class TxSinopac(models.Model):
_inherit = 'payment.transaction'
sinopac_txn_type = fields.Char('Transaction type')
@api.model
def _sinopac_get_sdk(self):
sinopac = self.env['payment.acquirer'].search([('provider', '=', 'sinopac')], limit=1)
return SinopacSDK(
shop_no=sinopac.shop_no,
key_a1=sinopac.sinopac_a1,
key_a2=sinopac.sinopac_a2,
key_b1=sinopac.sinopac_b1,
key_b2=sinopac.sinopac_b2
)
# --------------------------------------------------
# FORM RELATED METHODS
# --------------------------------------------------
@api.model
def _sinopac_form_get_tx_from_data(self, data):
# TODO 取得傳入參數
return {}
def _sinopac_form_get_invalid_parameters(self, data):
# TODO 取得無效參數
return
def _sinopac_form_validate(self, data):
# TODO 表單驗證
return True
sale.order
的欄位,把 Day 0x1A 的 order.sinopac
order_id
建立反向關聯/models/sale_order.py
# -*- coding: utf-8 -*-
from odoo import models, fields, api
class SaleOrder(models.Model):
_inherit = 'sale.order'
order_sinopac_ids = fields.One2many(
'order.sinopac',
'order_id',
string='永豐金流訂單'
)
__init__.py
增加關聯/models/__init__.py
from . import payment_acquirer
from . import payment_transaction
from . import sale_order
/views
建立 payment_templates.xml
,基本上這個檔案都沒什麼動,綠界跟 Paypal 有動的也只有 template id,要注意這裡的也是 noupdate="1"
喔/views/payment_templates.xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<template id="sinopac_form">
<div>
<input type="hidden" name="data_set" t-att-data-action-url="tx_url" data-remove-me=""/>
<t t-foreach="parameters" t-as="parameter">
<input type="hidden" t-att-name="parameter" t-att-value="parameter_value" />
</t>
</div>
</template>
</data>
</odoo>
payment_views.xml
,這個是在支付方式裡面的 form 顯示,畫面如下/views/payment_views.xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="payment_acquirer_sinopac" model="ir.ui.view">
<field name="name">payment_acquirer_sinopac</field>
<field name="model">payment.acquirer</field>
<field name="inherit_id" ref="payment.acquirer_form"/>
<field name="arch" type="xml">
<xpath expr="//notebook/page[1]" position="inside">
<group attrs="{'invisible': [('provider', '!=', 'sinopac')]}">
<group name="group_interface_setting" string="基本設定">
<field name="shop_no"/>
<field name="sinopac_a1"/>
<field name="sinopac_a2"/>
<field name="sinopac_b1"/>
<field name="sinopac_b2"/>
</group>
<group name="group_domain_setting" string="網域">
<field name="domain"/>
</group>
</group>
</xpath>
</field>
</record>
<record id="inherit_view_order_form_sinopac" model="ir.ui.view">
<field name="name">inherit_view_order_form_sinopac</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<xpath expr="//notebook" position="inside">
<page name="sinopac_order_line" string="永豐金流訂單資訊" groups="payment_sinopac.group_manager">
<field name="order_sinopac_ids">
<tree create="false" delete="false" editable="top">
<field name="prdt_name" readonly="1"/>
<field name="ts_no" readonly="1"/>
<field name="ts_date" readonly="1"/>
<field name="pay_date" readonly="1"/>
<field name="amount" readonly="1"/>
<field name="description" readonly="1"/>
<field name="pay_status" readonly="1"/>
<field name="pay_type" readonly="1"/>
</tree>
</field>
</page>
</xpath>
</field>
</record>
</data>
</odoo>
/views/payment_order_templates.xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="assets_frontend" inherit_id="web.assets_common" name="assets_frontend_payment_sinopac">
<xpath expr="." position="inside">
<script type="text/javascript" src="/payment_sinopac/static/src/js/selection.js"/>
</xpath>
</template>
<template id="sinopac_payment_type" name="sinopac payment type" inherit_id="payment.payment_tokens_list">
<xpath expr="//div[hasclass('text-muted')]" position="before">
<t t-if="acq.provider =='sinopac'">
<div class="form-group">
<select class="form-control" id="sinopac_payment_method" name="sinopac_payment_method">
<option value="A">ATM 轉帳</option>
<option value="C">信用卡</option>
</select>
</div>
</t>
</xpath>
</template>
</odoo>
hold_payment_type
函數使用時機/static/src/js/selection.js
'use strict';
odoo.define('payment_sinopac.create_order', function (require) {
var ajax = require('web.ajax');
async function chose_payment_type(payment_type) {
let response = await ajax.jsonRpc(
'/payment/sinopac/hold_payment_type',
'call',
{
payment: payment_type
});
console.log(response);
return true;
}
$(document).ready(function () {
let payment_list = document.querySelectorAll('div[class*="o_payment_acquirer_select"]'),
hold_payment = false;
if (payment_list) {
payment_list.forEach(item => {
let provider = item.querySelector('input[name="pm_id"]'),
payment = provider.dataset.provider;
if (payment === 'sinopac') {
item.onclick = async function (event) {
let payment_method = document.querySelector('#sinopac_payment_method');
if (payment_method) {
hold_payment = await chose_payment_type(payment_method.value);
}
}
}
});
}
console.log('sinopac js init!');
});
});
__manifest__.py
註冊 xml 位置,這裡直接提供 data 的陣列給各位/__manifest__.py
"data": [
'security/payment_sinopac_access_rule.xml',
'security/ir.model.access.csv',
'views/payment_views.xml',
'views/payment_templates.xml',
'views/payment_order_templates.xml',
'views/payment_order_views.xml',
'data/payment_acquirer_data.xml',
],
記得重新安裝模組,然後到設定
-> 使用者
-> Admin
設定模組權限才看的到
記得把支付方式
的永豐金流支付
設定成測試模式
,並輸入自己的商店代號
跟key
odoo addons 目前就是開發到這樣,odoo 電商付款那邊還要看一下底層怎麼跑,建立訂單有開api測試過是正常的,明天就是整個鐵人賽的總結了,不會有程式,簡單回顧一下這30天的心路歷程。