coveralls: 
[![Coverage Status](https://coveralls.io/builds/8301375/badge)](https://coveralls.io/builds/8301375)

Coverage increased (+0.03%) to 96.838% when pulling **24133bcd29248dc6ffe3b280de287fa99d356153 on bugfix/84** into **a4738be03b9cc0680d24466b7eefe5fdde6b7d2d on develop**.

suligap: Looks good. Could you add a relevant entry to the CHANGELOG please?
MarkusH: This might actually not finally solve the issue (need to look into it again), but we do some more casting in `sort_params()`. This needs to be reworked.
coveralls: 
[![Coverage Status](https://coveralls.io/builds/8321514/badge)](https://coveralls.io/builds/8321514)

Coverage decreased (-0.03%) to 96.787% when pulling **60c77ad543749d54ee27b625bc7d5dccc1828198 on bugfix/84** into **a4738be03b9cc0680d24466b7eefe5fdde6b7d2d on develop**.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/8338167/badge)](https://coveralls.io/builds/8338167)

Coverage decreased (-0.03%) to 96.787% when pulling **5adc95c90fa7b86fd8debd47d347339963ec7ffc on bugfix/84** into **a4738be03b9cc0680d24466b7eefe5fdde6b7d2d on develop**.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/8623388/badge)](https://coveralls.io/builds/8623388)

Coverage decreased (-0.03%) to 96.787% when pulling **820e3994a2eee44e93658ef588cdcf1739c88690 on bugfix/84** into **a4738be03b9cc0680d24466b7eefe5fdde6b7d2d on develop**.

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8484c44..532fd0e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,11 @@
 # Changelog
 
 
-## Unreleased
+## 4.6.0 (under development)
+
+* Fixed encoding issues when passing byte string parameters on Python 3
+  ([#84](https://github.com/laterpay/laterpay-client-python/issues/84))
+
 
 ## 4.5.0
 
diff --git a/laterpay/signing.py b/laterpay/signing.py
index 24b1bf2..347aa31 100644
--- a/laterpay/signing.py
+++ b/laterpay/signing.py
@@ -53,12 +53,8 @@ def sort_params(param_dict):
     for name, value_list in six.iteritems(param_dict):
         if isinstance(value_list, (list, tuple)):
             for value in value_list:
-                if not isinstance(value, six.string_types):
-                    value = str(value)
                 param_list.append((name, value))
         else:
-            if not isinstance(value_list, six.string_types):
-                value_list = str(value_list)
             param_list.append((name, value_list))
 
     return sorted(param_list)
@@ -127,10 +123,13 @@ def create_base_message(params, url, method='POST'):
 
         values_str = []
 
-        # If any non six.string_types objects, ``str()`` them.
         for value in values:
-            if not isinstance(value, six.string_types):
+            if not isinstance(value, (six.string_types, six.binary_type)):
+                # If any non-string or non-bytes like objects, ``str()`` them.
                 value = str(value)
+            if six.PY3 and isinstance(value, six.binary_type):
+                # Issue #84, decode byte strings before using them on Python 3
+                value = value.decode()
             values_str.append(value)
 
         data[key] = [quote(compat.encode_if_unicode(value_str), safe='') for value_str in values_str]

Decode byte strings before using them during signing
On Python 3, when singing a request containing a byte string (`b'foo'`) the value is casted into a string, resulting in `"b'foo'"`, instead of properly decoding the string to `'foo'`.

diff --git a/tests/test_signing.py b/tests/test_signing.py
index ecbff1f..2f0fe02 100644
--- a/tests/test_signing.py
+++ b/tests/test_signing.py
@@ -25,7 +25,7 @@ class TestSigningHelper(unittest.TestCase):
         )
 
     def test_create_message_bytestrings(self):
-        params = {'parĄm1': 'valuĘ'}
+        params = {b'par\xc4\x84m1': b'valu\xc4\x98'}
         url = 'https://endpoint.com/ąpi'
 
         msg = signing.create_base_message(params, url)

pip install -e .
parse_log_pytest
requirements.txt
flake8==2.6.0 coverage==4.1 pydocstyle==1.0.0 furl==0.4.95 mock==2.0.0 responses==0.5.1
3.5
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
certifi==2021.5.30
charset-normalizer==2.0.12
cookies==2.2.1
coverage==4.1
flake8==2.6.0
furl==0.4.95
idna==3.10
-e git+https://github.com/laterpay/laterpay-client-python.git@a4738be03b9cc0680d24466b7eefe5fdde6b7d2d#egg=laterpay_client
mccabe==0.5.3
mock==2.0.0
orderedmultidict==1.0.1
pbr==6.1.1
pycodestyle==2.0.0
pydocstyle==1.0.0
pyflakes==1.2.3
requests==2.27.1
responses==0.5.1
six==1.17.0
urllib3==1.26.20

name: laterpay-client-python
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - charset-normalizer==2.0.12
      - cookies==2.2.1
      - coverage==4.1
      - flake8==2.6.0
      - furl==0.4.95
      - idna==3.10
      - mccabe==0.5.3
      - mock==2.0.0
      - orderedmultidict==1.0.1
      - pbr==6.1.1
      - pycodestyle==2.0.0
      - pydocstyle==1.0.0
      - pyflakes==1.2.3
      - requests==2.27.1
      - responses==0.5.1
      - six==1.17.0
      - urllib3==1.26.20
prefix: /opt/conda/envs/laterpay-client-python

tests/test_signing.py::TestSigningHelper::test_create_message_bytestrings
tests/test_signing.py::TestSigningHelper::test_create_message_sorting_and_combining_params tests/test_signing.py::TestSigningHelper::test_create_message_unicode tests/test_signing.py::TestSigningHelper::test_create_message_wrong_method tests/test_signing.py::TestSigningHelper::test_normalise_param_structure tests/test_signing.py::TestSigningHelper::test_sign tests/test_signing.py::TestSigningHelper::test_sign_and_encode tests/test_signing.py::TestSigningHelper::test_signing_with_item tests/test_signing.py::TestSigningHelper::test_url_verification tests/test_signing.py::TestSigningHelper::test_verify_invalid_unicode_signature tests/test_signing.py::TestSigningHelper::test_verify_str_signature tests/test_signing.py::TestSigningHelper::test_verify_unicode_signature
CHANGELOG.md laterpay/signing.py
CHANGELOG.md laterpay/signing.py