Guida all'utilizzo di NoPos Connect
Questa guida ti accompagnerà passo dopo passo nell'integrazione di NoPos Connect nella tua applicazione.
Suggerimento
Questa guida assume che tu abbia già familiarità con OAuth 2.0. Se è la tua prima volta, ti consigliamo di leggere prima l'introduzione a NoPos Connect.
Prerequisiti
- Account NoPos attivo
- Applicazione registrata nella dashboard NoPos
- Conoscenza base di OAuth 2.0
Setup iniziale
1. Registra la tua applicazione
Accedi alla dashboard NoPos e crea una nuova applicazione:
- Vai su Impostazioni → Applicazioni
- Clicca Nuova Applicazione
- Compila i dettagli:
- Nome: Nome della tua app
- Descrizione: Breve descrizione
- Domini consentiti: Domini autorizzati per i redirect URI (es.
example.com,app.example.com) - Scopes: Permessi richiesti
2. Ottieni le credenziali
Dopo la creazione, riceverai:
- Client ID: Identificatore pubblico della tua app
- Client Secret: Chiave segreta (mantienila sicura!)
Implementazione
Flusso di autorizzazione
1. Reindirizza l'utente alla pagina di autenticazione
Reindirizza l'utente direttamente alla pagina di autenticazione di NoPos con i parametri OAuth:
- JavaScript
- Python
- PHP
function generateConnectUrl() {
const params = new URLSearchParams({
client_id: 'YOUR_CLIENT_ID',
redirect_uri: 'https://your-app.com/callback',
scopes: 'payments:read payments:write fiscals:read',
state: generateRandomState()
});
// Reindirizza alla pagina di connessione di NoPos
return `https://app.nopos.it/connect?${params}`;
}
function generateRandomState() {
return crypto.randomBytes(32).toString('hex');
}
// Utilizzo
window.location.href = generateConnectUrl();
import secrets
from urllib.parse import urlencode
def generate_auth_url():
params = {
'client_id': 'YOUR_CLIENT_ID',
'redirect_uri': 'https://your-app.com/callback',
'scopes': 'payments:read payments:write fiscals:read',
'state': generate_random_state()
}
# Reindirizza alla pagina di connessione di NoPos
return f"https://app.nopos.it/connect?{urlencode(params)}"
def generate_random_state():
return secrets.token_urlsafe(32)
# Utilizzo (in Flask)
from flask import redirect
@app.route('/connect')
def connect():
return redirect(generate_connect_url())
<?php
function generateAuthUrl() {
$params = [
'client_id' => 'YOUR_CLIENT_ID',
'redirect_uri' => 'https://your-app.com/callback',
'scopes' => 'payments:read payments:write fiscals:read',
'state' => generateRandomState()
];
// Reindirizza alla pagina di connessione di NoPos
return 'https://app.nopos.it/connect?' . http_build_query($params);
}
function generateRandomState() {
return bin2hex(random_bytes(32));
}
// Utilizzo
header('Location: ' . generateConnectUrl());
exit;
?>
2. Gestisci il callback e scambia il code per il token
Il callback riceverà un code (authorization code) invece del token diretto. Devi scambiare questo code con il token usando il tuo client_secret.
- JavaScript/Express
- Python/Flask
- PHP
// Express.js example
app.get('/callback', async (req, res) => {
const { code, state, error } = req.query;
// Gestisci errori
if (error) {
console.error('OAuth error:', error);
return res.redirect('/error?message=' + encodeURIComponent(req.query.error_description));
}
// Valida state per sicurezza CSRF
if (!validateState(state)) {
return res.status(400).send('Invalid state parameter');
}
// Scambia il code per il token
try {
const tokenResponse = await fetch('https://api.nopos.it/v1/auth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
code: code,
client_id: process.env.NOPOS_CLIENT_ID,
client_secret: process.env.NOPOS_CLIENT_SECRET,
redirect_uri: 'https://your-app.com/callback'
})
});
if (!tokenResponse.ok) {
throw new Error('Token exchange failed');
}
const tokenData = await tokenResponse.json();
// Salva il token
const sessionTokenData = {
access_token: tokenData.accessToken,
refresh_token: tokenData.refreshToken
};
saveTokenToSession(req.session, sessionTokenData);
res.redirect('/dashboard');
} catch (error) {
console.error('Token exchange error:', error);
res.redirect('/error?message=Token exchange failed');
}
});
function validateState(state) {
// Implementa la validazione dello state
// Confronta con quello salvato in sessione
return true; // Placeholder
}
from flask import Flask, request, redirect, session
import requests
import time
import os
app = Flask(__name__)
@app.route('/callback')
def callback():
code = request.args.get('code')
state = request.args.get('state')
error = request.args.get('error')
# Gestisci errori
if error:
print(f'OAuth error: {error}')
return redirect(f'/error?message={error}')
# Valida state per sicurezza CSRF
if not validate_state(state):
return 'Invalid state parameter', 400
# Scambia il code per il token
try:
token_response = requests.post('https://api.nopos.it/v1/auth/token', json={
'code': code,
'client_id': os.getenv('NOPOS_CLIENT_ID'),
'client_secret': os.getenv('NOPOS_CLIENT_SECRET'),
'redirect_uri': 'https://your-app.com/callback'
})
token_response.raise_for_status()
token_data = token_response.json()
# Salva il token
session_token_data = {
'access_token': token_data['accessToken'],
'refresh_token': token_data['refreshToken']
}
save_token_to_session(session, session_token_data)
return redirect('/dashboard')
except Exception as e:
print(f'Token exchange error: {e}')
return redirect('/error?message=Token exchange failed')
def validate_state(state):
# Implementa la validazione dello state
# Confronta con quello salvato in sessione
return True # Placeholder
<?php
// PHP example
if (isset($_GET['code'])) {
$code = $_GET['code'];
$state = $_GET['state'];
$error = $_GET['error'] ?? null;
// Gestisci errori
if ($error) {
error_log("OAuth error: " . $error);
header("Location: /error?message=" . urlencode($error));
exit;
}
// Valida state per sicurezza CSRF
if (!validateState($state)) {
http_response_code(400);
echo "Invalid state parameter";
exit;
}
// Scambia il code per il token
$ch = curl_init('https://api.nopos.it/v1/auth/token');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'code' => $code,
'client_id' => getenv('NOPOS_CLIENT_ID'),
'client_secret' => getenv('NOPOS_CLIENT_SECRET'),
'redirect_uri' => 'https://your-app.com/callback'
]));
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
header("Location: /error?message=Token exchange failed");
exit;
}
$tokenData = json_decode($response, true);
// Salva il token
$sessionTokenData = [
'access_token' => $tokenData['accessToken'],
'refresh_token' => $tokenData['refreshToken']
];
saveTokenToSession($_SESSION, $sessionTokenData);
header('Location: /dashboard');
}
function validateState($state) {
// Implementa la validazione dello state
// Confronta con quello salvato in sessione
return true; // Placeholder
}
?>
3. Usa il token per le API
- JavaScript
- Python
- PHP
class NoPosClient {
constructor(token) {
this.token = token;
this.baseUrl = 'https://api.nopos.it/v1';
}
async makeRequest(endpoint, options = {}) {
const url = `${this.baseUrl}${endpoint}`;
const headers = {
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json',
...options.headers
};
const response = await fetch(url, {
...options,
headers
});
if (!response.ok) {
throw new Error(`API Error: ${response.status} ${response.statusText}`);
}
return response.json();
}
// Esempi di utilizzo
async getPayments() {
return this.makeRequest('/payments');
}
async createPayment(paymentData) {
return this.makeRequest('/payments', {
method: 'POST',
body: JSON.stringify(paymentData)
});
}
async getInvoices() {
return this.makeRequest('/fiscal/invoices');
}
}
// Utilizzo
const client = new NoPosClient(accessToken);
const payments = await client.getPayments();
import requests
from typing import Dict, Any
class NoPosClient:
def __init__(self, token: str):
self.token = token
self.base_url = 'https://api.nopos.it/v1'
def make_request(self, endpoint: str, method: str = 'GET', data: Dict = None) -> Dict[str, Any]:
url = f"{self.base_url}{endpoint}"
headers = {
'Authorization': f'Bearer {self.token}',
'Content-Type': 'application/json'
}
response = requests.request(method, url, headers=headers, json=data)
response.raise_for_status()
return response.json()
# Esempi di utilizzo
def get_payments(self):
return self.make_request('/payments')
def create_payment(self, payment_data):
return self.make_request('/payments', 'POST', payment_data)
def get_invoices(self):
return self.make_request('/fiscal/invoices')
# Utilizzo
client = NoPosClient(access_token)
payments = client.get_payments()
<?php
class NoPosClient {
private $token;
private $baseUrl;
public function __construct($token) {
$this->token = $token;
$this->baseUrl = 'https://api.nopos.it/v1';
}
public function makeRequest($endpoint, $method = 'GET', $data = null) {
$url = $this->baseUrl . $endpoint;
$headers = [
'Authorization: Bearer ' . $this->token,
'Content-Type: application/json'
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
if ($data) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode >= 400) {
throw new Exception("API Error: " . $httpCode);
}
return json_decode($response, true);
}
// Esempi di utilizzo
public function getPayments() {
return $this->makeRequest('/payments');
}
public function createPayment($paymentData) {
return $this->makeRequest('/payments', 'POST', $paymentData);
}
public function getInvoices() {
return $this->makeRequest('/fiscal/invoices');
}
}
// Utilizzo
$client = new NoPosClient($accessToken);
$payments = $client->getPayments();
?>
Gestione del token
Refresh automatico
class TokenManager {
constructor() {
this.token = null;
this.refreshTimer = null;
}
setToken(tokenData) {
this.token = tokenData;
// Imposta refresh automatico 5 minuti prima della scadenza
const refreshTime = tokenData.expires_at - (5 * 60 * 1000);
const timeUntilRefresh = refreshTime - Date.now();
if (timeUntilRefresh > 0) {
this.refreshTimer = setTimeout(() => {
this.refreshToken();
}, timeUntilRefresh);
}
}
async refreshToken() {
try {
// Implementa il refresh del token
const newToken = await this.requestNewToken();
this.setToken(newToken);
} catch (error) {
console.error('Token refresh failed:', error);
// Reindirizza al login
this.redirectToAuth();
}
}
async requestNewToken() {
// Implementa la richiesta di un nuovo token
// Questo dipende dalla tua implementazione
throw new Error('Not implemented');
}
redirectToAuth() {
window.location.href = generateConnectUrl();
}
}
Storage sicuro
// Per applicazioni web - usa sessionStorage
function saveTokenToSession(tokenData) {
sessionStorage.setItem('nopos_token', JSON.stringify(tokenData));
}
function getTokenFromSession() {
const tokenData = sessionStorage.getItem('nopos_token');
if (!tokenData) return null;
const parsed = JSON.parse(tokenData);
// Controlla se il token è scaduto
if (Date.now() >= parsed.expires_at) {
sessionStorage.removeItem('nopos_token');
return null;
}
return parsed;
}
// Per applicazioni mobile - usa storage sicuro
// React Native example
import AsyncStorage from '@react-native-async-storage/async-storage';
async function saveTokenSecure(tokenData) {
await AsyncStorage.setItem('nopos_token', JSON.stringify(tokenData));
}
async function getTokenSecure() {
const tokenData = await AsyncStorage.getItem('nopos_token');
if (!tokenData) return null;
const parsed = JSON.parse(tokenData);
if (Date.now() >= parsed.expires_at) {
await AsyncStorage.removeItem('nopos_token');
return null;
}
return parsed;
}
Esempi pratici
E-commerce Integration
class EcommerceIntegration {
constructor(noposClient) {
this.client = noposClient;
}
async processOrder(orderData) {
try {
// 1. Crea il pagamento
const payment = await this.client.createPayment({
amount: orderData.total * 100, // Converti in centesimi
currency: 'EUR',
description: `Ordine #${orderData.id}`,
metadata: {
order_id: orderData.id,
customer_email: orderData.customer.email
}
});
// 2. Crea la fattura
const invoice = await this.client.createInvoice({
customer: orderData.customer,
items: orderData.items,
payment_id: payment.id
});
// 3. Invia conferma
await this.sendOrderConfirmation(orderData.customer.email, {
order: orderData,
payment: payment,
invoice: invoice
});
return { payment, invoice };
} catch (error) {
console.error('Order processing failed:', error);
throw error;
}
}
async sendOrderConfirmation(email, data) {
// Implementa l'invio email
console.log('Sending confirmation to:', email);
}
}
Dashboard Analytics
class AnalyticsDashboard {
constructor(noposClient) {
this.client = noposClient;
}
async getDashboardData() {
try {
const [payments, invoices, webhooks] = await Promise.all([
this.client.getPayments({ limit: 100 }),
this.client.getInvoices({ limit: 100 }),
this.client.getWebhooks()
]);
return {
totalRevenue: payments.data.reduce((sum, p) => sum + p.amount, 0),
totalInvoices: invoices.data.length,
activeWebhooks: webhooks.data.filter(w => w.active).length,
recentPayments: payments.data.slice(0, 10),
recentInvoices: invoices.data.slice(0, 10)
};
} catch (error) {
console.error('Failed to load dashboard data:', error);
throw error;
}
}
}
Testing
Ambiente di test
// Configurazione per test
const TEST_CONFIG = {
client_id: 'test_client_id',
client_secret: 'test_client_secret',
redirect_uri: 'http://localhost:3000/callback',
base_url: 'https://api.nopos.it/v1'
};
// Mock per test
class MockNoPosClient {
constructor() {
this.calls = [];
}
async makeRequest(endpoint, options = {}) {
this.calls.push({ endpoint, options });
// Restituisci dati mock
if (endpoint === '/payments') {
return {
data: [
{ id: '1', amount: 1000, status: 'completed' },
{ id: '2', amount: 2000, status: 'pending' }
]
};
}
return { data: [] };
}
}
// Test example
describe('NoPos Integration', () => {
let client;
beforeEach(() => {
client = new MockNoPosClient();
});
test('should fetch payments', async () => {
const payments = await client.makeRequest('/payments');
expect(payments.data).toHaveLength(2);
expect(client.calls).toHaveLength(1);
});
});
Troubleshooting
Problemi comuni
Token scaduto
if (error.status === 401) {
// Token scaduto, reindirizza al login
redirectToAuth();
}
Scope insufficienti
if (error.status === 403) {
// Permessi insufficienti
console.error('Insufficient permissions. Required scopes:', error.required_scopes);
}
Rate limiting
if (error.status === 429) {
// Troppe richieste, aspetta
const retryAfter = error.headers['retry-after'];
setTimeout(() => retryRequest(), retryAfter * 1000);
}
Debug
// Abilita logging dettagliato
const DEBUG = process.env.NODE_ENV === 'development';
class NoPosClient {
async makeRequest(endpoint, options = {}) {
if (DEBUG) {
console.log('NoPos API Request:', {
endpoint,
method: options.method || 'GET',
headers: options.headers
});
}
const response = await fetch(url, options);
if (DEBUG) {
console.log('NoPos API Response:', {
status: response.status,
headers: Object.fromEntries(response.headers.entries())
});
}
return response.json();
}
}
Risorse aggiuntive
NoPos Connect - Integrazione semplice, risultati potenti