exantrix-api 1.0.0

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.
Files changed (5) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +9 -0
  3. data/README.md +40 -0
  4. data/lib/exantrix_api.rb +109 -0
  5. metadata +50 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fc4ce62e9bfedb47b4ee01c13f3ee2825e5660a1ac516d2491a5e7b4192c6397
4
+ data.tar.gz: 413501453c50381e5bdb19701c39a4bf2fc3547bc98d262a8672040bdb0fd94f
5
+ SHA512:
6
+ metadata.gz: a09375acfefbfdc912beaa01178fb79872631802cea13c294a60b0089e645abca000038f272aebad6626b2eae5016fffb364243591e6fc5905dcddac5500cfab
7
+ data.tar.gz: 7619fa289231277ee53a4131dd5e72f0561412972a5124821cc344b72c33099fa708d2489b0792f8fafa60ddf8b8ffd9943b1b501769c011470fb6e9066adc9a
data/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 Exantrix
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # exantrix-api (Ruby)
2
+
3
+ Client Ruby (2.6+, bibliothèque standard seulement) de l'API vendeur de la marketplace [Exantrix](https://exantrix.com) (impression 3D, DTF, textile personnalisé, flocage, découpe) : produits, stock, commandes, expédition, et vérification des webhooks signés.
4
+
5
+ - Documentation de l'API : https://exantrix.com/extensions/api (FR) · https://exantrix.com/en/extensions/api (EN)
6
+ - Jeton API : généré dans votre [espace vendeur](https://exantrix.com/a2/vendeur)
7
+
8
+ ```bash
9
+ gem install exantrix-api
10
+ ```
11
+
12
+ ```ruby
13
+ require "exantrix_api"
14
+
15
+ ex = ExantrixApi::Client.new(ENV["EXANTRIX_JETON"])
16
+ ex.moi # compte, statut, commission
17
+ r = ex.envoyer([ # création / mise à jour par référence
18
+ { "reference" => "SKU-001", "titre" => "Support casque imprimé en 3D", "categorie" => "3d", "prix_ttc" => 19.90,
19
+ "stock" => 12, "images" => ["https://maboutique.fr/img/support.jpg"], "description" => "Support de casque en PLA" },
20
+ ])
21
+ ex.stock("SKU-001", 7)
22
+ commandes = ex.commandes("2026-01-01")
23
+ ex.expedier(commandes[0]["id"], "Colissimo", "6A12345678901")
24
+ ```
25
+
26
+ Webhook de commande (Rails / Sinatra) :
27
+
28
+ ```ruby
29
+ corps = request.body.read
30
+ halt 401 unless ExantrixApi.signature_valide?(corps, request.env["HTTP_X_EXANTRIX_SIGNATURE"], ENV["EXANTRIX_JETON"])
31
+ donnees = JSON.parse(corps) # donnees["evenement"] == "commande.payee"
32
+ ```
33
+
34
+ Les nouveaux produits sont validés par Exantrix avant mise en ligne. Limite : 600 requêtes par 10 minutes. Tests : `ruby -Ilib test/test_exantrix_api.rb`.
35
+
36
+ ## Les extensions Exantrix
37
+
38
+ [WordPress / WooCommerce](https://github.com/tony-dev-web/exantrix-marketplace-wordpress) · [PrestaShop](https://github.com/tony-dev-web/exantrix-marketplace-prestashop) · [Shopify](https://github.com/tony-dev-web/exantrix-marketplace-shopify) · [Magento 2](https://github.com/tony-dev-web/exantrix-marketplace-magento) · [Drupal Commerce](https://github.com/tony-dev-web/exantrix-marketplace-drupal) · [CSV, Odoo, Dolibarr](https://github.com/tony-dev-web/exantrix-marketplace-connecteurs) · [Postman / OpenAPI](https://github.com/tony-dev-web/exantrix-api) · [Node.js](https://github.com/tony-dev-web/exantrix-api-js) · [Python](https://github.com/tony-dev-web/exantrix-api-python) · [PHP](https://github.com/tony-dev-web/exantrix-api-php) · https://exantrix.com/extensions/
39
+
40
+ Licence MIT.
@@ -0,0 +1,109 @@
1
+ # Client Ruby de l'API vendeur Exantrix (https://exantrix.com/extensions/api).
2
+ # Ruby 2.6+, bibliotheque standard seulement (net/http, json, openssl).
3
+ require "net/http"
4
+ require "json"
5
+ require "openssl"
6
+ require "uri"
7
+
8
+ module ExantrixApi
9
+ VERSION = "1.0.0".freeze
10
+ BASE = "https://exantrix.com/api/v1".freeze
11
+ LOT_MAX = 200
12
+
13
+ class Error < StandardError
14
+ attr_reader :status, :body
15
+
16
+ def initialize(message, status = 0, body = {})
17
+ super(message)
18
+ @status = status
19
+ @body = body
20
+ end
21
+ end
22
+
23
+ # Verifie X-Exantrix-Signature ("sha256=" + HMAC-SHA256 hex du corps brut, cle = jeton).
24
+ def self.signature_valide?(corps_brut, entete, jeton)
25
+ attendu = "sha256=" + OpenSSL::HMAC.hexdigest("SHA256", jeton.to_s, corps_brut.to_s)
26
+ entete = entete.to_s
27
+ return false unless entete.bytesize == attendu.bytesize
28
+ diff = 0
29
+ attendu.bytes.zip(entete.bytes) { |a, b| diff |= a ^ b }
30
+ diff.zero?
31
+ end
32
+
33
+ class Client
34
+ # transport : lambda(methode, url, entetes, corps) -> [status, texte], pour les tests
35
+ def initialize(jeton, base: BASE, timeout: 60, transport: nil)
36
+ raise Error, "jeton API manquant" if jeton.to_s.empty?
37
+ @jeton = jeton
38
+ @base = base
39
+ @timeout = timeout
40
+ @transport = transport || method(:http)
41
+ end
42
+
43
+ def moi
44
+ appel("GET", "/moi")
45
+ end
46
+
47
+ def produits
48
+ appel("GET", "/produits")["produits"] || []
49
+ end
50
+
51
+ # Cree ou met a jour des produits par reference, par lots. Renvoie {"produits" => [...], "erreurs" => [...]}.
52
+ def envoyer(fiches, lot = 100)
53
+ produits = []
54
+ erreurs = []
55
+ fiches.each_slice([[lot, 1].max, LOT_MAX].min) do |tranche|
56
+ r = appel("PUT", "/produits", "produits" => tranche)
57
+ produits.concat(r["produits"] || [])
58
+ erreurs.concat(r["erreurs"] || [])
59
+ end
60
+ { "produits" => produits, "erreurs" => erreurs }
61
+ end
62
+
63
+ def stock(reference, stock)
64
+ appel("PATCH", "/produits/#{URI.encode_www_form_component(reference)}/stock", "stock" => [stock.to_i, 0].max)
65
+ end
66
+
67
+ def retirer(reference)
68
+ appel("DELETE", "/produits/#{URI.encode_www_form_component(reference)}")
69
+ end
70
+
71
+ def commandes(depuis = nil)
72
+ chemin = depuis ? "/commandes?depuis=#{URI.encode_www_form_component(depuis)}" : "/commandes"
73
+ appel("GET", chemin)["commandes"] || []
74
+ end
75
+
76
+ def expedier(commande_id, transporteur, suivi)
77
+ appel("POST", "/commandes/#{commande_id.to_i}/expedier", "transporteur" => transporteur, "suivi" => suivi)
78
+ end
79
+
80
+ def signature_valide?(corps_brut, entete)
81
+ ExantrixApi.signature_valide?(corps_brut, entete, @jeton)
82
+ end
83
+
84
+ private
85
+
86
+ def appel(methode, chemin, corps = nil)
87
+ entetes = { "Authorization" => "Bearer #{@jeton}", "Content-Type" => "application/json", "User-Agent" => "exantrix-api-ruby/#{VERSION}" }
88
+ status, texte = @transport.call(methode, @base + chemin, entetes, corps && JSON.generate(corps))
89
+ json = begin
90
+ JSON.parse(texte.to_s)
91
+ rescue JSON::ParserError
92
+ {}
93
+ end
94
+ json = {} unless json.is_a?(Hash)
95
+ raise Error.new(json["erreur"] || "HTTP #{status}", status, json) if status.to_i >= 400
96
+ json
97
+ end
98
+
99
+ def http(methode, url, entetes, corps)
100
+ uri = URI(url)
101
+ requete = Net::HTTP.const_get(methode.capitalize).new(uri, entetes)
102
+ requete.body = corps if corps
103
+ reponse = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", open_timeout: @timeout, read_timeout: @timeout) { |h| h.request(requete) }
104
+ [reponse.code.to_i, reponse.body]
105
+ rescue SystemCallError, Net::OpenTimeout, Net::ReadTimeout => e
106
+ raise Error, "Exantrix injoignable : #{e.message}"
107
+ end
108
+ end
109
+ end
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: exantrix-api
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Exantrix
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-09-19 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Produits, stock, commandes, expedition et verification des webhooks signes
14
+ de la marketplace exantrix.com. Bibliotheque standard seulement.
15
+ email:
16
+ - contact@exantrix.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - LICENSE
22
+ - README.md
23
+ - lib/exantrix_api.rb
24
+ homepage: https://exantrix.com/extensions/api
25
+ licenses:
26
+ - MIT
27
+ metadata:
28
+ source_code_uri: https://github.com/tony-dev-web/exantrix-api-ruby
29
+ documentation_uri: https://exantrix.com/extensions/api
30
+ homepage_uri: https://exantrix.com
31
+ post_install_message:
32
+ rdoc_options: []
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '2.6'
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ requirements: []
46
+ rubygems_version: 3.0.3.1
47
+ signing_key:
48
+ specification_version: 4
49
+ summary: Client de l'API vendeur Exantrix (marketplace impression 3D, DTF, textile)
50
+ test_files: []