Skip to content

Commit b8a751c

Browse files
authored
Merge pull request #1270 from trojikman/11.0-payment_wechat
commit is created by 👷‍♂️ Merge Bot: https://odoo-devops.readthedocs.io/en/latest/git/github-merge-bot.html
2 parents 46253b9 + 393c4f9 commit b8a751c

File tree

12 files changed

+335
-0
lines changed

12 files changed

+335
-0
lines changed

payment_wechat/README.rst

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
.. image:: https://img.shields.io/badge/license-MIT-blue.svg
2+
:target: https://opensource.org/licenses/MIT
3+
:alt: License: MIT
4+
5+
=================
6+
WeChat payments
7+
=================
8+
9+
Technical module to integrate WeChat payments with odoo POS, eCommerce or backend. As in WeChat QR codes are used, addional modules are required to show QR code in POS or eCommerce. Following methods are supported:
10+
11+
* TODO User scans QR and authorise payment
12+
* TODO User opens eCommerce website via WeChat's browser, fills the cart and is redirected to WeChat App UI to authorise the payment
13+
14+
Note, that this module doesn't implement *Quick Pay* method, i.e. the one where buyer shows QR code and vendor scans.
15+
16+
Credits
17+
=======
18+
19+
Contributors
20+
------------
21+
* `Ivan Yelizariev <https://it-projects.info/team/yelizariev>`__
22+
23+
Sponsors
24+
--------
25+
* `IT-Projects LLC <https://it-projects.info>`__
26+
27+
Maintainers
28+
-----------
29+
* `IT-Projects LLC <https://it-projects.info>`__
30+
31+
To get a guaranteed support
32+
you are kindly requested to purchase the module
33+
at `odoo apps store <https://apps.odoo.com/apps/modules/11.0/payment_wechat/>`__.
34+
35+
Thank you for understanding!
36+
37+
`IT-Projects Team <https://www.it-projects.info/team>`__
38+
39+
Further information
40+
===================
41+
42+
Demo: http://runbot.it-projects.info/demo/misc-addons/11.0
43+
44+
HTML Description: https://apps.odoo.com/apps/modules/11.0/payment_wechat/
45+
46+
Usage instructions: `<doc/index.rst>`_
47+
48+
Changelog: `<doc/changelog.rst>`_
49+
50+
Notifications on updates: `via Atom <https://github.com/it-projects-llc/misc-addons/commits/11.0/payment_wechat.atom>`_, `by Email <https://blogtrottr.com/?subscribe=https://github.com/it-projects-llc/misc-addons/commits/11.0/payment_wechat.atom>`_
51+
52+
Tested on Odoo 11.0 4d0a1330e05bd688265bea14df4ad12838f9f2d7

payment_wechat/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from . import models
2+
from . import controllers

payment_wechat/__manifest__.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Copyright 2018 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
2+
# License MIT (https://opensource.org/licenses/MIT).
3+
{
4+
"name": """WeChat payments""",
5+
"summary": """The most popular Chinese payment method""",
6+
"category": "Accounting",
7+
# "live_test_url": "",
8+
"images": [],
9+
"version": "11.0.1.0.0",
10+
"application": False,
11+
"author": "IT-Projects LLC, Ivan Yelizariev",
12+
"support": "[email protected]",
13+
"website": "https://it-projects.info/team/yelizariev",
14+
"license": "Other OSI approved licence", # MIT
15+
# "price": 9.00,
16+
# "currency": "EUR",
17+
"depends": [],
18+
"external_dependencies": {"python": [], "bin": []},
19+
"data": [],
20+
"demo": ["demo/w_p_demo.xml"],
21+
"qweb": [],
22+
"post_load": None,
23+
"pre_init_hook": None,
24+
"post_init_hook": None,
25+
"uninstall_hook": None,
26+
"auto_install": False,
27+
"installable": False,
28+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import p_w_controllers
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
from __future__ import unicode_literals
2+
3+
import logging
4+
import random
5+
import time
6+
7+
import requests
8+
9+
import odoo
10+
from odoo.http import request
11+
12+
_logger = logging.getLogger(__name__)
13+
14+
try:
15+
from odoo.addons.bus.controllers.main import BusController
16+
except ImportError:
17+
_logger.error("pos_multi_session_sync inconsisten with odoo version")
18+
BusController = object
19+
20+
21+
class Controller(BusController):
22+
@odoo.http.route("/wechat/getsignkey", type="json", auth="public")
23+
def getSignKey(self, message):
24+
data = {}
25+
data["mch_id"] = request.env["ir.config_parameter"].get_param("wechat.mchId")
26+
wcc = request.env["wechat.config"]
27+
data["nonce_str"] = (wcc.getRandomNumberGeneration(message))[:32]
28+
data["sign"] = (
29+
str(time.time()).replace(".", "")
30+
+ "{:010}".format(random.randint(1, 9999999999))
31+
+ "{:010}".format(random.randint(1, 9999999999))
32+
)[:32]
33+
post = wcc.makeXmlPost(data)
34+
url = "https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey"
35+
r1 = requests.post(url, data=post, timeout=30)
36+
message = {}
37+
message["resp1"] = r1.text
38+
return message
39+
40+
@odoo.http.route("/wechat/test", type="json", auth="public")
41+
def testAccessToken(self, message):
42+
wcc = request.env["wechat.config"]
43+
if not wcc:
44+
wcc = wcc.create({"token_validity": 7000, "access_token": "test"})
45+
wcc.getAccessToken()
46+
47+
@odoo.http.route("/wechat/payment_commence", type="json", auth="public")
48+
def micropay(self, message):
49+
# data = message['data']
50+
# data['order_id'] = '{0:06}'.format(message['data']['order_id'])
51+
# data['cashier_id'] = '{0:05}'.format(message['data']['cashier_id'])
52+
# data['session_id'] = '{0:05}'.format(message['data']['session_id'])
53+
data = {}
54+
data["auth_code"] = message["data"]["auth_code"]
55+
data["appid"] = request.env["ir.config_parameter"].get_param("wechat.appId")
56+
data["mch_id"] = request.env["ir.config_parameter"].get_param("wechat.mchId")
57+
data["body"] = message["data"]["order_short"]
58+
59+
data["out_trade_no"] = (
60+
str(time.time()).replace(".", "")
61+
+ "{:010}".format(random.randint(1, 9999999999))
62+
+ "{:010}".format(random.randint(1, 9999999999))
63+
)[:32]
64+
wcc = request.env["wechat.config"]
65+
if not wcc:
66+
wcc = wcc.create({"token_validity": 1, "access_token": ""})
67+
data["total_fee"] = message["data"]["total_fee"]
68+
data["spbill_create_ip"] = wcc.getIpList()[0]
69+
# data['auth_code'] = message['data']['auth_code']
70+
#
71+
# device_info =
72+
# sign_type =
73+
# detail =
74+
# attach =
75+
# fee_type =
76+
# goods_tag =
77+
# limit_pay =
78+
# scene_info =
79+
#
80+
data["nonce_str"] = (wcc.getRandomNumberGeneration(message))[:32]
81+
data["sign"] = (wcc.getRandomNumberGeneration(message))[:32]
82+
83+
post = wcc.makeXmlPost(data)
84+
r1 = requests.post(
85+
"https://api.mch.weixin.qq.com/sandboxnew/pay/micropay",
86+
data=post,
87+
timeout=30,
88+
)
89+
message = {}
90+
message["resp1"] = r1
91+
message["resp_text1"] = r1.text
92+
message["resp_cont1"] = r1.content
93+
# message['encode_text1'] = r1.text.encode('iso-8859-1').decode('utf-8')
94+
# print(r1.text.encode('utf-8'))
95+
time.sleep(5)
96+
# return request.redirect('/wechat/payment_query')
97+
#
98+
# @odoo.http.route('/wechat/payment_query', type="json", auth="public")
99+
# def queryOrderApi(self, message):
100+
data_qa = {}
101+
data_qa["appid"] = data["appid"]
102+
data_qa["mch_id"] = data["mch_id"]
103+
data_qa["out_trade_no"] = data["out_trade_no"]
104+
data_qa["nonce_str"] = data["nonce_str"]
105+
data_qa["sign"] = data["sign"]
106+
if hasattr(data, "sign_type"):
107+
data_qa["sign_type"] = data["sign_type"]
108+
109+
post = wcc.makeXmlPost(data_qa)
110+
r2 = requests.post(
111+
"https://api.mch.weixin.qq.com/sandboxnew/pay/orderquery",
112+
data=post,
113+
timeout=30,
114+
)
115+
message["resp2"] = r2
116+
message["resp_text2"] = r2.text
117+
message["resp_cont2"] = r2.content
118+
# message['encode_text2'] = r2.text.encode('iso-8859-1').decode('utf-8')
119+
# with open('txt.txt', 'w+') as fil:
120+
# fil.write(r1.text, r2.text)
121+
# print(r2.text.encode('utf-8'))
122+
# for each_unicode_character in r2.text.encode('utf-8').decode('utf-8'):
123+
# print(each_unicode_character)
124+
# print(message['encode_text1'])
125+
# print(message['encode_text2'])
126+
return message

payment_wechat/demo/w_p_demo.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<odoo>
3+
<!--<record id="wechat_journal" model="account.journal">-->
4+
<!--<field name="name">Wechat - Test</field>-->
5+
<!--<field name="code">TWC</field>-->
6+
<!--<field name="type">bank</field>-->
7+
<!--<field name="default_debit_account_id" ref="bnk"/>-->
8+
<!--<field name="default_credit_account_id" ref="bnk"/>-->
9+
<!--<field name="wechat_payment">True</field>-->
10+
<!--</record>-->
11+
</odoo>

payment_wechat/doc/changelog.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
`1.0.0`
2+
-------
3+
4+
- Init version

payment_wechat/doc/index.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
=================
2+
WeChat payments
3+
=================
4+
5+
Follow instructions of `WeChat API <https://apps.odoo.com/apps/modules/11.0/wechat/>`__.
6+
7+
Usage
8+
=====
9+
10+
Following instruction covers backend usage only. For POS and eCommerce use instructions of corresponding modules.
11+
12+
* open menu TODO

payment_wechat/models/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import wechat_models
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
from __future__ import absolute_import, unicode_literals
2+
3+
import hashlib
4+
import json
5+
import time
6+
7+
import requests
8+
9+
from odoo import api, fields, models
10+
from odoo.http import request
11+
12+
13+
class AccountJournal(models.Model):
14+
_inherit = "account.journal"
15+
16+
wechat_payment = fields.Boolean(
17+
string="Allow WeChat payments",
18+
default=False,
19+
help="Check this box if this account allows pay via WeChat",
20+
)
21+
22+
23+
# class PosOrder(models.Model):
24+
# _inherit = "pos.order"
25+
#
26+
# auth_code = fields.Integer(string='Code obtained from customers QR or BarCode', default=0)
27+
28+
29+
class WechatConfiguration(models.Model):
30+
_name = "wechat.config"
31+
32+
# auth_code = fields.Integer(string='Code obtained from customers QR or BarCode', default=0)
33+
access_token = fields.Char(string="access_token")
34+
token_validity = fields.Float(string="validity time")
35+
36+
@api.multi
37+
def getAccessToken(self):
38+
if not self.token_validity:
39+
self.createVals()
40+
if self.token_validity < time.time():
41+
appId = request.env["ir.config_parameter"].get_param("wechat.appId")
42+
appSecret = request.env["ir.config_parameter"].get_param("wechat.appSecret")
43+
url = (
44+
"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"
45+
% (appId, appSecret)
46+
)
47+
response = requests.get(url, timeout=30)
48+
access_token = json.loads(response.text)["access_token"]
49+
self.write(
50+
{"token_validity": time.time() + 7000, "access_token": access_token}
51+
)
52+
else:
53+
access_token = self.access_token
54+
return access_token
55+
56+
def getIpList(self):
57+
token = self.getAccessToken()
58+
url = "https://api.wechat.com/cgi-bin/getcallbackip?access_token=%s" % token
59+
response = requests.get(url, timeout=30)
60+
return json.loads(response.text)["ip_list"]
61+
62+
def sortData(self, message):
63+
arrA = []
64+
data = message["data"]
65+
for key in data:
66+
if data[key]:
67+
arrA.append(str(key) + "=" + str(data[key]))
68+
arrA.sort()
69+
return arrA
70+
71+
def getRandomNumberGeneration(self, message):
72+
data = self.sortData(message)
73+
strA = " & ".join(data)
74+
return hashlib.sha256(strA.encode("utf-8")).hexdigest().upper()
75+
76+
def makeXmlPost(self, data):
77+
xml_str = ["<xml>"]
78+
for key in sorted(data):
79+
if data[key]:
80+
xml_str.append(
81+
"<" + str(key) + ">" + str(data[key]) + "</" + str(key) + ">"
82+
)
83+
xml_str.append("</xml>")
84+
return "\n".join(xml_str)

0 commit comments

Comments
 (0)