proxy-seller-user-api 2.1.0__tar.gz → 2.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proxy_seller_user_api
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: Client library for the proxy-seller.com Client API v2
5
5
  Home-page: https://github.com/proxy-seller/user-api-python
6
6
  Author: proxy-seller
@@ -93,9 +93,18 @@ api.setPaymentCode('paddle_subscription')
93
93
  api.prolongMake('ipv4', ['1.2.3.4'], '1m', paymentId='balance') # the balance pays, not the card
94
94
  ```
95
95
 
96
- Within one level the server rule stays: when a call passes both, `paymentCode` wins over
97
- `paymentId`, and the same goes for the two setters. When one `Api` instance is shared between
98
- threads, pass the payment with each call instead of switching the default.
96
+ Within one call the server rule stays: when a call passes both, `paymentCode` wins over
97
+ `paymentId`. The two setters replace each other — the last one called wins, as in the Java, PHP
98
+ and Go SDKs:
99
+
100
+ ```python
101
+ api.setPaymentCode('paddle_subscription')
102
+ api.setPaymentId('balance') # the default is now the balance; the card code is cleared
103
+ ```
104
+
105
+ Before 2.1.1 the code set by `setPaymentCode()` outlived a later `setPaymentId()`, so the switch
106
+ above kept charging the saved card. When one `Api` instance is shared between threads, pass the
107
+ payment with each call instead of switching the default.
99
108
 
100
109
  `balancePaymentsList()` is not the place to pick an order payment from: it lists the systems for
101
110
  topping the balance up with `balanceAdd()` and never includes the balance itself. That top-up is
@@ -659,6 +668,19 @@ Downloads return a file, not the envelope: `text/plain` for `txt` and custom tem
659
668
  `proxyDownload('subresident', package_key='KEY')` for a subpackage and
660
669
  `proxyDownloadResident(id=...)` for the main package.
661
670
 
671
+ Residential lines take the connection scheme in `protocol` (`http` or `socks5`) and the gateway
672
+ in `host`. With `txt` a line becomes `socks5://login:password@host:port`, `csv` keeps its
673
+ columns, and a custom template gets the value in `%protocol%`; without `protocol` the lines have
674
+ no scheme, as before. Login, password and ports are the same for both protocols. `socks5` also
675
+ carries UDP and HTTP/3 (QUIC) to websites. Gateways: `res.proxy-seller.com` (Europe, the
676
+ default), `us.res.proxy-seller.com`, `asia.res.proxy-seller.com` — pick the one closest to your
677
+ server. This is not `proto`, which selects IPv4 ports:
678
+
679
+ ```python
680
+ api.proxyDownloadResident(id='LIST_ID', protocol='socks5', host='us.res.proxy-seller.com')
681
+ api.proxyDownload('subresident', ext='txt', package_key='KEY', protocol='http')
682
+ ```
683
+
662
684
  ## Resident and subuser lists
663
685
 
664
686
  `resident/lists` returns a **flat array** (no `items` wrapper).
@@ -713,6 +735,14 @@ api.residentSubUserListDelete('PACKAGE_KEY', 561) # {'status': 'delete'} | {'st
713
735
  `*Code` options are optional; `rotationId` never takes a code.
714
736
  - `authActive(id, "Y")` became `authChange(id, True)`.
715
737
  - `ping()` and `proxyCheck()` have no v2 equivalent.
738
+ - API errors raise `ApiError`, which subclasses `Exception`, not `ValueError`. 1.x raised a bare
739
+ `ValueError` with the first message, so `except ValueError:` no longer catches API errors —
740
+ catch `ApiError`; it carries `code`, `errors` and `http_status`. `ValueError` / `TypeError`
741
+ come from the SDK's own argument checks, before anything is sent.
742
+ - There is no default payment system. 1.x sent `paymentId=1` on orders and `29` from
743
+ `balanceAdd()` — v1 numeric ids that v2 refuses (`Incorrect payment system`). Call
744
+ `setPaymentCode('balance')` (or `'paddle_subscription'`) or pass the payment per call;
745
+ `balanceAdd()` takes an id from `balancePaymentsList()`.
716
746
  - `residentListDelete()` sends the ID in the request body.
717
747
  - `balanceAdd()` uses its explicit `paymentId`, then falls back to `setPaymentId()`;
718
748
  `paymentCode` is not accepted here.
@@ -785,6 +815,18 @@ Changes made after the 2.0 release, in the order the server shipped them:
785
815
  - **The API key no longer appears in errors.** A network error used to carry the request URL,
786
816
  key included, in its message and in the chained `requests` exception — see
787
817
  [The API key in errors and logs](#the-api-key-in-errors-and-logs).
818
+ - **2.1.1: the two payment setters replace each other.** `setPaymentId()` after
819
+ `setPaymentCode()`, or the other way round, switches the client's default: the last call wins,
820
+ as in the Java, PHP and Go SDKs. Before, the code outlived a later `setPaymentId()`, so
821
+ `setPaymentCode('paddle_subscription')` followed by `setPaymentId('balance')` still charged the
822
+ saved card.
823
+ - **2.1.1: `{type}` is URL-encoded in the path** of `reference/list`, `proxy/list`,
824
+ `proxy/download`, `prolong/*` and `autoprolong/*`, as in the other SDKs. A `/` in the value
825
+ used to split the path, and the request queue then took a `prolong/make` call for a read.
826
+ - **Unreleased: `protocol` and `host` in residential exports.** `proxyDownloadResident()` takes
827
+ `protocol=` and `host=`, and `proxyDownload('subresident', ...)` passes them on as filters: the
828
+ connection scheme (`http` / `socks5`) and the gateway in the lines. Without them the request and
829
+ the lines are the same as before — see [Proxies](#proxies).
788
830
 
789
831
  ## Tests
790
832
 
@@ -49,9 +49,18 @@ api.setPaymentCode('paddle_subscription')
49
49
  api.prolongMake('ipv4', ['1.2.3.4'], '1m', paymentId='balance') # the balance pays, not the card
50
50
  ```
51
51
 
52
- Within one level the server rule stays: when a call passes both, `paymentCode` wins over
53
- `paymentId`, and the same goes for the two setters. When one `Api` instance is shared between
54
- threads, pass the payment with each call instead of switching the default.
52
+ Within one call the server rule stays: when a call passes both, `paymentCode` wins over
53
+ `paymentId`. The two setters replace each other — the last one called wins, as in the Java, PHP
54
+ and Go SDKs:
55
+
56
+ ```python
57
+ api.setPaymentCode('paddle_subscription')
58
+ api.setPaymentId('balance') # the default is now the balance; the card code is cleared
59
+ ```
60
+
61
+ Before 2.1.1 the code set by `setPaymentCode()` outlived a later `setPaymentId()`, so the switch
62
+ above kept charging the saved card. When one `Api` instance is shared between threads, pass the
63
+ payment with each call instead of switching the default.
55
64
 
56
65
  `balancePaymentsList()` is not the place to pick an order payment from: it lists the systems for
57
66
  topping the balance up with `balanceAdd()` and never includes the balance itself. That top-up is
@@ -615,6 +624,19 @@ Downloads return a file, not the envelope: `text/plain` for `txt` and custom tem
615
624
  `proxyDownload('subresident', package_key='KEY')` for a subpackage and
616
625
  `proxyDownloadResident(id=...)` for the main package.
617
626
 
627
+ Residential lines take the connection scheme in `protocol` (`http` or `socks5`) and the gateway
628
+ in `host`. With `txt` a line becomes `socks5://login:password@host:port`, `csv` keeps its
629
+ columns, and a custom template gets the value in `%protocol%`; without `protocol` the lines have
630
+ no scheme, as before. Login, password and ports are the same for both protocols. `socks5` also
631
+ carries UDP and HTTP/3 (QUIC) to websites. Gateways: `res.proxy-seller.com` (Europe, the
632
+ default), `us.res.proxy-seller.com`, `asia.res.proxy-seller.com` — pick the one closest to your
633
+ server. This is not `proto`, which selects IPv4 ports:
634
+
635
+ ```python
636
+ api.proxyDownloadResident(id='LIST_ID', protocol='socks5', host='us.res.proxy-seller.com')
637
+ api.proxyDownload('subresident', ext='txt', package_key='KEY', protocol='http')
638
+ ```
639
+
618
640
  ## Resident and subuser lists
619
641
 
620
642
  `resident/lists` returns a **flat array** (no `items` wrapper).
@@ -669,6 +691,14 @@ api.residentSubUserListDelete('PACKAGE_KEY', 561) # {'status': 'delete'} | {'st
669
691
  `*Code` options are optional; `rotationId` never takes a code.
670
692
  - `authActive(id, "Y")` became `authChange(id, True)`.
671
693
  - `ping()` and `proxyCheck()` have no v2 equivalent.
694
+ - API errors raise `ApiError`, which subclasses `Exception`, not `ValueError`. 1.x raised a bare
695
+ `ValueError` with the first message, so `except ValueError:` no longer catches API errors —
696
+ catch `ApiError`; it carries `code`, `errors` and `http_status`. `ValueError` / `TypeError`
697
+ come from the SDK's own argument checks, before anything is sent.
698
+ - There is no default payment system. 1.x sent `paymentId=1` on orders and `29` from
699
+ `balanceAdd()` — v1 numeric ids that v2 refuses (`Incorrect payment system`). Call
700
+ `setPaymentCode('balance')` (or `'paddle_subscription'`) or pass the payment per call;
701
+ `balanceAdd()` takes an id from `balancePaymentsList()`.
672
702
  - `residentListDelete()` sends the ID in the request body.
673
703
  - `balanceAdd()` uses its explicit `paymentId`, then falls back to `setPaymentId()`;
674
704
  `paymentCode` is not accepted here.
@@ -741,6 +771,18 @@ Changes made after the 2.0 release, in the order the server shipped them:
741
771
  - **The API key no longer appears in errors.** A network error used to carry the request URL,
742
772
  key included, in its message and in the chained `requests` exception — see
743
773
  [The API key in errors and logs](#the-api-key-in-errors-and-logs).
774
+ - **2.1.1: the two payment setters replace each other.** `setPaymentId()` after
775
+ `setPaymentCode()`, or the other way round, switches the client's default: the last call wins,
776
+ as in the Java, PHP and Go SDKs. Before, the code outlived a later `setPaymentId()`, so
777
+ `setPaymentCode('paddle_subscription')` followed by `setPaymentId('balance')` still charged the
778
+ saved card.
779
+ - **2.1.1: `{type}` is URL-encoded in the path** of `reference/list`, `proxy/list`,
780
+ `proxy/download`, `prolong/*` and `autoprolong/*`, as in the other SDKs. A `/` in the value
781
+ used to split the path, and the request queue then took a `prolong/make` call for a read.
782
+ - **Unreleased: `protocol` and `host` in residential exports.** `proxyDownloadResident()` takes
783
+ `protocol=` and `host=`, and `proxyDownload('subresident', ...)` passes them on as filters: the
784
+ connection scheme (`http` / `socks5`) and the gateway in the lines. Without them the request and
785
+ the lines are the same as before — see [Proxies](#proxies).
744
786
 
745
787
  ## Tests
746
788
 
@@ -614,6 +614,10 @@ class Api:
614
614
  paymentCode, пара setPaymentId()/setPaymentCode() в этот запрос не подмешивается вовсе
615
615
  (см. _payment_for).
616
616
 
617
+ setPaymentId() и setPaymentCode() вытесняют друг друга: выигрывает последний вызов, как
618
+ в Java, PHP и Go. Раньше код клиента был старше id, и setPaymentId('balance') после
619
+ setPaymentCode('paddle_subscription') оставлял оплату привязанной картой.
620
+
617
621
  Заказы и продления оплачиваются только балансом или привязанной картой: здесь
618
622
  подходят 'balance' и 'paddle_subscription' — код (сервер, не найдя ObjectId,
619
623
  резолвит значение как код) либо ObjectId одной из этих двух систем; остальные
@@ -623,6 +627,8 @@ class Api:
623
627
  (самого баланса в этом списке нет), см. balanceAdd.
624
628
  """
625
629
  self.paymentId = id
630
+ if id is not None:
631
+ self.paymentCode = None
626
632
 
627
633
  def getPaymentId(self):
628
634
  return self.paymentId
@@ -634,8 +640,12 @@ class Api:
634
640
 
635
641
  Значение ПО УМОЛЧАНИЮ для клиента, как и setPaymentId(): платёжка, переданная в вызов
636
642
  (paymentId или paymentCode), главнее, и тогда пара клиента не уходит вовсе.
643
+
644
+ Сбрасывает setPaymentId(): выигрывает последний из двух вызовов (см. setPaymentId).
637
645
  """
638
646
  self.paymentCode = code
647
+ if code is not None:
648
+ self.paymentId = None
639
649
 
640
650
  def getPaymentCode(self):
641
651
  return self.paymentCode
@@ -1150,7 +1160,7 @@ class Api:
1150
1160
  """
1151
1161
  if type is None:
1152
1162
  return self.request('GET', 'reference/list')
1153
- return self.request('GET', 'reference/list/' + str(type))
1163
+ return self.request('GET', 'reference/list/' + self._segment(type))
1154
1164
 
1155
1165
  def prepare(self, **kwargs):
1156
1166
  return self.filterNone(dict(kwargs))
@@ -1169,6 +1179,16 @@ class Api:
1169
1179
  """Задано ли значение для сервера: None и строка из одних пробелов — "не задано"."""
1170
1180
  return value is not None and str(value).strip() != ''
1171
1181
 
1182
+ @staticmethod
1183
+ def _segment(value):
1184
+ """
1185
+ Значение {type} для пути — URL-кодированным одним сегментом, как в Java, PHP и Go.
1186
+
1187
+ Без кодирования '/' в значении дробил путь на лишние сегменты, и очередь относила
1188
+ такой вызов не к своей категории (prolong/make/{type} — к чтению с его мягким разбором).
1189
+ """
1190
+ return quote(str(value), safe='')
1191
+
1172
1192
  def _payment_for(self, values, fields=('paymentId', 'paymentCode')):
1173
1193
  """
1174
1194
  Платёжка клиента для тела запроса — с учётом платёжки самого вызова.
@@ -1897,7 +1917,7 @@ class Api:
1897
1917
  mix_isp — все активные прокси выбранных заказов.
1898
1918
  """
1899
1919
  values = self._order_options(options, prolong_options)
1900
- return self.request('POST', 'prolong/calc/' + type,
1920
+ return self.request('POST', 'prolong/calc/' + self._segment(type),
1901
1921
  json=self.prepareProlong(ids, periodId, coupon, values, type))
1902
1922
 
1903
1923
  def prolongMake(self, type, ids=None, periodId=None, coupon='', options=None, **prolong_options):
@@ -1934,7 +1954,7 @@ class Api:
1934
1954
  # в errors[{code:16}], разбирается общим кодом конверта. Единственным оставшимся её
1935
1955
  # эффектом было превращать ЛЕГИТИМНЫЙ "status: success" с пустым orderId в фальшивую
1936
1956
  # ошибку — уже ПОСЛЕ списания денег, потеряв total/balance/listBaseOrderNumbers.
1937
- return self.request('POST', 'prolong/make/' + type,
1957
+ return self.request('POST', 'prolong/make/' + self._segment(type),
1938
1958
  json=self.prepareProlong(ids, periodId, coupon, values, type))
1939
1959
 
1940
1960
  # --------------------------- Auto prolong ---------------------------
@@ -2078,7 +2098,7 @@ class Api:
2078
2098
  values = self._order_options(options, autoprolong_options)
2079
2099
  payload = self.prepareAutoProlong(ids, periodId, values, type)
2080
2100
  self._assert_auto_prolong_payment(payload)
2081
- return self.request('POST', 'autoprolong/calc/' + type, json=payload)
2101
+ return self.request('POST', 'autoprolong/calc/' + self._segment(type), json=payload)
2082
2102
 
2083
2103
  def autoProlongEnable(self, type, ids=None, periodId=None, options=None, **autoprolong_options):
2084
2104
  """
@@ -2109,7 +2129,7 @@ class Api:
2109
2129
  values = self._order_options(options, autoprolong_options)
2110
2130
  payload = self.prepareAutoProlong(ids, periodId, values, type)
2111
2131
  self._assert_auto_prolong_payment(payload)
2112
- return self.request('POST', 'autoprolong/enable/' + type, json=payload)
2132
+ return self.request('POST', 'autoprolong/enable/' + self._segment(type), json=payload)
2113
2133
 
2114
2134
  def autoProlongDisable(self, type, ids=None, options=None, **autoprolong_options):
2115
2135
  """
@@ -2139,7 +2159,7 @@ class Api:
2139
2159
  """
2140
2160
  self._assert_auto_prolong_type(type)
2141
2161
  values = self._order_options(options, autoprolong_options)
2142
- return self.request('POST', 'autoprolong/disable/' + type,
2162
+ return self.request('POST', 'autoprolong/disable/' + self._segment(type),
2143
2163
  json=self.prepareAutoProlong(ids, None, values, type))
2144
2164
 
2145
2165
  # --------------------------- Proxy ---------------------------
@@ -2169,7 +2189,7 @@ class Api:
2169
2189
  params = self.filterNone(filters)
2170
2190
  if type is None:
2171
2191
  return self.request('GET', 'proxy/list', params=params)
2172
- return self.request('GET', 'proxy/list/' + str(type), params=params)
2192
+ return self.request('GET', 'proxy/list/' + self._segment(type), params=params)
2173
2193
 
2174
2194
  def proxyDownload(self, type, ext=None, proto=None, listId=None, **filters):
2175
2195
  """
@@ -2186,7 +2206,10 @@ class Api:
2186
2206
  csv — '%ip%;%port%;%login%;%password%'). Ограничения см. assertExt().
2187
2207
  proto (str): https | socks5 | None (иные значения сервер игнорирует)
2188
2208
  listId (str): only for resident-family types
2189
- filters: package_key (ТОЛЬКО subresident), country (код страны), ends
2209
+ filters: package_key (ТОЛЬКО subresident), country (код страны), ends,
2210
+ protocol и host — схема (http | socks5) и шлюз в строках резидентов, только
2211
+ resident и subresident (см. proxyDownloadResident()). protocol — это не proto:
2212
+ proto выбирает порты IPv4
2190
2213
 
2191
2214
  Note:
2192
2215
  Для резидентских листов основного пакета используйте proxyDownloadResident().
@@ -2204,9 +2227,10 @@ class Api:
2204
2227
  "for the main resident package use proxyDownloadResident().")
2205
2228
  params = {'ext': self.assertExt(ext), 'proto': proto, 'listId': listId}
2206
2229
  params.update(filters)
2207
- return self.request('GET', 'proxy/download/' + type, params=self.filterNone(params))
2230
+ return self.request('GET', 'proxy/download/' + self._segment(type),
2231
+ params=self.filterNone(params))
2208
2232
 
2209
- def proxyDownloadResident(self, id=None, ext=None, maxLine=None):
2233
+ def proxyDownloadResident(self, id=None, ext=None, maxLine=None, protocol=None, host=None):
2210
2234
  """
2211
2235
  Export the resident proxy list (файл, attachment).
2212
2236
 
@@ -2219,12 +2243,20 @@ class Api:
2219
2243
  это не ObjectId.
2220
2244
  ext (str): txt | csv | кастомный шаблон (см. proxyDownload)
2221
2245
  maxLine (int): Maximum number of lines.
2246
+ protocol (str): http | socks5. При txt строка становится
2247
+ ``socks5://login:password@host:port``, csv не меняется, кастомный шаблон
2248
+ получает значение в %protocol%; None — строки без схемы, как раньше. Логин,
2249
+ пароль и порты у обоих протоколов одни; socks5 несёт ещё UDP и HTTP/3 (QUIC)
2250
+ к сайтам. Не путать с proto у proxyDownload(): тот выбирает порты IPv4.
2251
+ host (str): шлюз — res.proxy-seller.com (по умолчанию), us.res.proxy-seller.com,
2252
+ asia.res.proxy-seller.com.
2222
2253
 
2223
2254
  Returns:
2224
2255
  str: The exported proxies.
2225
2256
  """
2226
2257
  return self.request('GET', 'proxy/download/resident', params=self.filterNone(
2227
- {'id': id, 'ext': self.assertExt(ext), 'maxLine': maxLine}))
2258
+ {'id': id, 'ext': self.assertExt(ext), 'maxLine': maxLine,
2259
+ 'protocol': protocol, 'host': host}))
2228
2260
 
2229
2261
  #: Допустимые значения type у proxy/replace — ПРИЧИНА замены.
2230
2262
  PROXY_REPLACE_TYPES = (
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proxy_seller_user_api
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: Client library for the proxy-seller.com Client API v2
5
5
  Home-page: https://github.com/proxy-seller/user-api-python
6
6
  Author: proxy-seller
@@ -93,9 +93,18 @@ api.setPaymentCode('paddle_subscription')
93
93
  api.prolongMake('ipv4', ['1.2.3.4'], '1m', paymentId='balance') # the balance pays, not the card
94
94
  ```
95
95
 
96
- Within one level the server rule stays: when a call passes both, `paymentCode` wins over
97
- `paymentId`, and the same goes for the two setters. When one `Api` instance is shared between
98
- threads, pass the payment with each call instead of switching the default.
96
+ Within one call the server rule stays: when a call passes both, `paymentCode` wins over
97
+ `paymentId`. The two setters replace each other — the last one called wins, as in the Java, PHP
98
+ and Go SDKs:
99
+
100
+ ```python
101
+ api.setPaymentCode('paddle_subscription')
102
+ api.setPaymentId('balance') # the default is now the balance; the card code is cleared
103
+ ```
104
+
105
+ Before 2.1.1 the code set by `setPaymentCode()` outlived a later `setPaymentId()`, so the switch
106
+ above kept charging the saved card. When one `Api` instance is shared between threads, pass the
107
+ payment with each call instead of switching the default.
99
108
 
100
109
  `balancePaymentsList()` is not the place to pick an order payment from: it lists the systems for
101
110
  topping the balance up with `balanceAdd()` and never includes the balance itself. That top-up is
@@ -659,6 +668,19 @@ Downloads return a file, not the envelope: `text/plain` for `txt` and custom tem
659
668
  `proxyDownload('subresident', package_key='KEY')` for a subpackage and
660
669
  `proxyDownloadResident(id=...)` for the main package.
661
670
 
671
+ Residential lines take the connection scheme in `protocol` (`http` or `socks5`) and the gateway
672
+ in `host`. With `txt` a line becomes `socks5://login:password@host:port`, `csv` keeps its
673
+ columns, and a custom template gets the value in `%protocol%`; without `protocol` the lines have
674
+ no scheme, as before. Login, password and ports are the same for both protocols. `socks5` also
675
+ carries UDP and HTTP/3 (QUIC) to websites. Gateways: `res.proxy-seller.com` (Europe, the
676
+ default), `us.res.proxy-seller.com`, `asia.res.proxy-seller.com` — pick the one closest to your
677
+ server. This is not `proto`, which selects IPv4 ports:
678
+
679
+ ```python
680
+ api.proxyDownloadResident(id='LIST_ID', protocol='socks5', host='us.res.proxy-seller.com')
681
+ api.proxyDownload('subresident', ext='txt', package_key='KEY', protocol='http')
682
+ ```
683
+
662
684
  ## Resident and subuser lists
663
685
 
664
686
  `resident/lists` returns a **flat array** (no `items` wrapper).
@@ -713,6 +735,14 @@ api.residentSubUserListDelete('PACKAGE_KEY', 561) # {'status': 'delete'} | {'st
713
735
  `*Code` options are optional; `rotationId` never takes a code.
714
736
  - `authActive(id, "Y")` became `authChange(id, True)`.
715
737
  - `ping()` and `proxyCheck()` have no v2 equivalent.
738
+ - API errors raise `ApiError`, which subclasses `Exception`, not `ValueError`. 1.x raised a bare
739
+ `ValueError` with the first message, so `except ValueError:` no longer catches API errors —
740
+ catch `ApiError`; it carries `code`, `errors` and `http_status`. `ValueError` / `TypeError`
741
+ come from the SDK's own argument checks, before anything is sent.
742
+ - There is no default payment system. 1.x sent `paymentId=1` on orders and `29` from
743
+ `balanceAdd()` — v1 numeric ids that v2 refuses (`Incorrect payment system`). Call
744
+ `setPaymentCode('balance')` (or `'paddle_subscription'`) or pass the payment per call;
745
+ `balanceAdd()` takes an id from `balancePaymentsList()`.
716
746
  - `residentListDelete()` sends the ID in the request body.
717
747
  - `balanceAdd()` uses its explicit `paymentId`, then falls back to `setPaymentId()`;
718
748
  `paymentCode` is not accepted here.
@@ -785,6 +815,18 @@ Changes made after the 2.0 release, in the order the server shipped them:
785
815
  - **The API key no longer appears in errors.** A network error used to carry the request URL,
786
816
  key included, in its message and in the chained `requests` exception — see
787
817
  [The API key in errors and logs](#the-api-key-in-errors-and-logs).
818
+ - **2.1.1: the two payment setters replace each other.** `setPaymentId()` after
819
+ `setPaymentCode()`, or the other way round, switches the client's default: the last call wins,
820
+ as in the Java, PHP and Go SDKs. Before, the code outlived a later `setPaymentId()`, so
821
+ `setPaymentCode('paddle_subscription')` followed by `setPaymentId('balance')` still charged the
822
+ saved card.
823
+ - **2.1.1: `{type}` is URL-encoded in the path** of `reference/list`, `proxy/list`,
824
+ `proxy/download`, `prolong/*` and `autoprolong/*`, as in the other SDKs. A `/` in the value
825
+ used to split the path, and the request queue then took a `prolong/make` call for a read.
826
+ - **Unreleased: `protocol` and `host` in residential exports.** `proxyDownloadResident()` takes
827
+ `protocol=` and `host=`, and `proxyDownload('subresident', ...)` passes them on as filters: the
828
+ connection scheme (`http` / `socks5`) and the gateway in the lines. Without them the request and
829
+ the lines are the same as before — see [Proxies](#proxies).
788
830
 
789
831
  ## Tests
790
832
 
@@ -12,7 +12,7 @@ setup(
12
12
  name='proxy_seller_user_api',
13
13
  # 2.x = Client API v2 (baseUrl .../personal/api/v2/, ObjectId-строки, конверт
14
14
  # {status, data, errors}). 2.1 добавляет balance/autotopup/get и /set.
15
- version='2.1.0',
15
+ version='2.1.2',
16
16
  author='proxy-seller',
17
17
  author_email='support@proxy-seller.com',
18
18
  description='Client library for the proxy-seller.com Client API v2',
@@ -139,6 +139,26 @@ class ProxyDownloadTest(unittest.TestCase):
139
139
  api.proxyDownload('resident', ext='txt')
140
140
  self.assertEqual(session.last['params'], {'ext': 'txt'})
141
141
 
142
+ def test_resident_protocol_and_host_are_sent(self):
143
+ api, session = make_api([text_file('socks5://a:b@us.res.proxy-seller.com:10000')])
144
+ api.proxyDownloadResident('561', 'txt', 10, protocol='socks5', host='us.res.proxy-seller.com')
145
+ call = session.last
146
+ self.assertTrue(call['url'].endswith('proxy/download/resident'))
147
+ self.assertEqual(call['params'], {'id': '561', 'ext': 'txt', 'maxLine': 10,
148
+ 'protocol': 'socks5', 'host': 'us.res.proxy-seller.com'})
149
+
150
+ def test_resident_without_protocol_and_host_is_unchanged(self):
151
+ api, session = make_api([text_file('a:b@res.proxy-seller.com:10000')])
152
+ api.proxyDownloadResident('561', 'txt', 10)
153
+ self.assertEqual(session.last['params'], {'id': '561', 'ext': 'txt', 'maxLine': 10})
154
+
155
+ def test_subresident_protocol_and_host_are_sent(self):
156
+ api, session = make_api([text_file('http://a:b@asia.res.proxy-seller.com:10000')])
157
+ api.proxyDownload('subresident', ext='txt', package_key='PKG',
158
+ protocol='http', host='asia.res.proxy-seller.com')
159
+ self.assertEqual(session.last['params'], {'ext': 'txt', 'package_key': 'PKG',
160
+ 'protocol': 'http', 'host': 'asia.res.proxy-seller.com'})
161
+
142
162
  def test_ext_validation(self):
143
163
  with self.assertRaises(ValueError):
144
164
  Api.assertExt('a' * 251)