Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions payment_wechat/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
.. image:: https://img.shields.io/badge/license-MIT-blue.svg
:target: https://opensource.org/licenses/MIT
:alt: License: MIT

=================
WeChat payments
=================

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:

* TODO User scans QR and authorise payment
* TODO User opens eCommerce website via WeChat's browser, fills the cart and is redirected to WeChat App UI to authorise the payment

Note, that this module doesn't implement *Quick Pay* method, i.e. the one where buyer shows QR code and vendor scans.

Credits
=======

Contributors
------------
* `Ivan Yelizariev <https://it-projects.info/team/yelizariev>`__

Sponsors
--------
* `IT-Projects LLC <https://it-projects.info>`__

Maintainers
-----------
* `IT-Projects LLC <https://it-projects.info>`__

To get a guaranteed support
you are kindly requested to purchase the module
at `odoo apps store <https://apps.odoo.com/apps/modules/11.0/payment_wechat/>`__.

Thank you for understanding!

`IT-Projects Team <https://www.it-projects.info/team>`__

Further information
===================

Demo: http://runbot.it-projects.info/demo/misc-addons/11.0

HTML Description: https://apps.odoo.com/apps/modules/11.0/payment_wechat/

Usage instructions: `<doc/index.rst>`_

Changelog: `<doc/changelog.rst>`_

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>`_

Tested on Odoo 11.0 4d0a1330e05bd688265bea14df4ad12838f9f2d7
2 changes: 2 additions & 0 deletions payment_wechat/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import controllers
28 changes: 28 additions & 0 deletions payment_wechat/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Copyright 2018 Ivan Yelizariev <https://it-projects.info/team/yelizariev>
# License MIT (https://opensource.org/licenses/MIT).
{
"name": """WeChat payments""",
"summary": """The most popular Chinese payment method""",
"category": "Accounting",
# "live_test_url": "",
"images": [],
"version": "11.0.1.0.0",
"application": False,
"author": "IT-Projects LLC, Ivan Yelizariev",
"support": "[email protected]",
"website": "https://it-projects.info/team/yelizariev",
"license": "Other OSI approved licence", # MIT
# "price": 9.00,
# "currency": "EUR",
"depends": [],
"external_dependencies": {"python": [], "bin": []},
"data": [],
"demo": ["demo/w_p_demo.xml"],
"qweb": [],
"post_load": None,
"pre_init_hook": None,
"post_init_hook": None,
"uninstall_hook": None,
"auto_install": False,
"installable": False,
}
1 change: 1 addition & 0 deletions payment_wechat/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import p_w_controllers
126 changes: 126 additions & 0 deletions payment_wechat/controllers/p_w_controllers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
from __future__ import unicode_literals

import logging
import random
import time

import requests

import odoo
from odoo.http import request

_logger = logging.getLogger(__name__)

try:
from odoo.addons.bus.controllers.main import BusController
except ImportError:
_logger.error("pos_multi_session_sync inconsisten with odoo version")
BusController = object


class Controller(BusController):
@odoo.http.route("/wechat/getsignkey", type="json", auth="public")
def getSignKey(self, message):
data = {}
data["mch_id"] = request.env["ir.config_parameter"].get_param("wechat.mchId")
wcc = request.env["wechat.config"]
data["nonce_str"] = (wcc.getRandomNumberGeneration(message))[:32]
data["sign"] = (
str(time.time()).replace(".", "")
+ "{:010}".format(random.randint(1, 9999999999))
+ "{:010}".format(random.randint(1, 9999999999))
)[:32]
post = wcc.makeXmlPost(data)
url = "https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey"
r1 = requests.post(url, data=post, timeout=30)
message = {}
message["resp1"] = r1.text
return message

@odoo.http.route("/wechat/test", type="json", auth="public")
def testAccessToken(self, message):
wcc = request.env["wechat.config"]
if not wcc:
wcc = wcc.create({"token_validity": 7000, "access_token": "test"})
wcc.getAccessToken()

@odoo.http.route("/wechat/payment_commence", type="json", auth="public")
def micropay(self, message):
# data = message['data']
# data['order_id'] = '{0:06}'.format(message['data']['order_id'])
# data['cashier_id'] = '{0:05}'.format(message['data']['cashier_id'])
# data['session_id'] = '{0:05}'.format(message['data']['session_id'])
data = {}
data["auth_code"] = message["data"]["auth_code"]
data["appid"] = request.env["ir.config_parameter"].get_param("wechat.appId")
data["mch_id"] = request.env["ir.config_parameter"].get_param("wechat.mchId")
data["body"] = message["data"]["order_short"]

data["out_trade_no"] = (
str(time.time()).replace(".", "")
+ "{:010}".format(random.randint(1, 9999999999))
+ "{:010}".format(random.randint(1, 9999999999))
)[:32]
wcc = request.env["wechat.config"]
if not wcc:
wcc = wcc.create({"token_validity": 1, "access_token": ""})
data["total_fee"] = message["data"]["total_fee"]
data["spbill_create_ip"] = wcc.getIpList()[0]
# data['auth_code'] = message['data']['auth_code']
#
# device_info =
# sign_type =
# detail =
# attach =
# fee_type =
# goods_tag =
# limit_pay =
# scene_info =
#
data["nonce_str"] = (wcc.getRandomNumberGeneration(message))[:32]
data["sign"] = (wcc.getRandomNumberGeneration(message))[:32]

post = wcc.makeXmlPost(data)
r1 = requests.post(
"https://api.mch.weixin.qq.com/sandboxnew/pay/micropay",
data=post,
timeout=30,
)
message = {}
message["resp1"] = r1
message["resp_text1"] = r1.text
message["resp_cont1"] = r1.content
# message['encode_text1'] = r1.text.encode('iso-8859-1').decode('utf-8')
# print(r1.text.encode('utf-8'))
time.sleep(5)
# return request.redirect('/wechat/payment_query')
#
# @odoo.http.route('/wechat/payment_query', type="json", auth="public")
# def queryOrderApi(self, message):
data_qa = {}
data_qa["appid"] = data["appid"]
data_qa["mch_id"] = data["mch_id"]
data_qa["out_trade_no"] = data["out_trade_no"]
data_qa["nonce_str"] = data["nonce_str"]
data_qa["sign"] = data["sign"]
if hasattr(data, "sign_type"):
data_qa["sign_type"] = data["sign_type"]

post = wcc.makeXmlPost(data_qa)
r2 = requests.post(
"https://api.mch.weixin.qq.com/sandboxnew/pay/orderquery",
data=post,
timeout=30,
)
message["resp2"] = r2
message["resp_text2"] = r2.text
message["resp_cont2"] = r2.content
# message['encode_text2'] = r2.text.encode('iso-8859-1').decode('utf-8')
# with open('txt.txt', 'w+') as fil:
# fil.write(r1.text, r2.text)
# print(r2.text.encode('utf-8'))
# for each_unicode_character in r2.text.encode('utf-8').decode('utf-8'):
# print(each_unicode_character)
# print(message['encode_text1'])
# print(message['encode_text2'])
return message
11 changes: 11 additions & 0 deletions payment_wechat/demo/w_p_demo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!--<record id="wechat_journal" model="account.journal">-->
<!--<field name="name">Wechat - Test</field>-->
<!--<field name="code">TWC</field>-->
<!--<field name="type">bank</field>-->
<!--<field name="default_debit_account_id" ref="bnk"/>-->
<!--<field name="default_credit_account_id" ref="bnk"/>-->
<!--<field name="wechat_payment">True</field>-->
<!--</record>-->
</odoo>
4 changes: 4 additions & 0 deletions payment_wechat/doc/changelog.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
`1.0.0`
-------

- Init version
12 changes: 12 additions & 0 deletions payment_wechat/doc/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
=================
WeChat payments
=================

Follow instructions of `WeChat API <https://apps.odoo.com/apps/modules/11.0/wechat/>`__.

Usage
=====

Following instruction covers backend usage only. For POS and eCommerce use instructions of corresponding modules.

* open menu TODO
1 change: 1 addition & 0 deletions payment_wechat/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import wechat_models
84 changes: 84 additions & 0 deletions payment_wechat/models/wechat_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from __future__ import absolute_import, unicode_literals

import hashlib
import json
import time

import requests

from odoo import api, fields, models
from odoo.http import request


class AccountJournal(models.Model):
_inherit = "account.journal"

wechat_payment = fields.Boolean(
string="Allow WeChat payments",
default=False,
help="Check this box if this account allows pay via WeChat",
)


# class PosOrder(models.Model):
# _inherit = "pos.order"
#
# auth_code = fields.Integer(string='Code obtained from customers QR or BarCode', default=0)


class WechatConfiguration(models.Model):
_name = "wechat.config"

# auth_code = fields.Integer(string='Code obtained from customers QR or BarCode', default=0)
access_token = fields.Char(string="access_token")
token_validity = fields.Float(string="validity time")

@api.multi
def getAccessToken(self):
if not self.token_validity:
self.createVals()
if self.token_validity < time.time():
appId = request.env["ir.config_parameter"].get_param("wechat.appId")
appSecret = request.env["ir.config_parameter"].get_param("wechat.appSecret")
url = (
"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"
% (appId, appSecret)
)
response = requests.get(url, timeout=30)
access_token = json.loads(response.text)["access_token"]
self.write(
{"token_validity": time.time() + 7000, "access_token": access_token}
)
else:
access_token = self.access_token
return access_token

def getIpList(self):
token = self.getAccessToken()
url = "https://api.wechat.com/cgi-bin/getcallbackip?access_token=%s" % token
response = requests.get(url, timeout=30)
return json.loads(response.text)["ip_list"]

def sortData(self, message):
arrA = []
data = message["data"]
for key in data:
if data[key]:
arrA.append(str(key) + "=" + str(data[key]))
arrA.sort()
return arrA

def getRandomNumberGeneration(self, message):
data = self.sortData(message)
strA = " & ".join(data)
return hashlib.sha256(strA.encode("utf-8")).hexdigest().upper()

def makeXmlPost(self, data):
xml_str = ["<xml>"]
for key in sorted(data):
if data[key]:
xml_str.append(
"<" + str(key) + ">" + str(data[key]) + "</" + str(key) + ">"
)
xml_str.append("</xml>")
return "\n".join(xml_str)
Binary file added payment_wechat/static/description/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions payment_wechat/views/views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yelizariev нужна эта вьюшка? она в манифесте не подключена

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Хотя я думал что Динар будет ругаться, не ругается оказывается

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Да, нужна скорее всего. Не знаю почему не подключена

<record id="view_account_journal_form_inherited" model="ir.ui.view">
<field name="name">account.journal.form</field>
<field name="model">account.journal</field>
<field name="inherit_id" ref="account.view_account_journal_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='type']" position="after">
<!--<field name="wechat_payment" attrs="{'invisible':[('type', '!=', 'bank')]}"/>-->
<field name="wechat_payment" />
</xpath>
</field>
</record>
</odoo>