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
- Redirect URI: URL di ritorno dopo l'autorizzazione
- 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. Genera URL di autorizzazione
- JavaScript
- Python
- PHP
function generateAuthUrl() {
const params = new URLSearchParams({
client_id: 'YOUR_CLIENT_ID',
client_secret: 'YOUR_CLIENT_SECRET',
redirect_uri: 'https://your-app.com/callback',
scopes: 'payments:read payments:write fiscal:read',
state: generateRandomState(),
response_type: 'code'
});
return `https://app.nopos.it/auth?${params}`;
}
function generateRandomState() {
return crypto.randomBytes(32).toString('hex');
}
import secrets
from urllib.parse import urlencode
def generate_auth_url():
params = {
'client_id': 'YOUR_CLIENT_ID',
'client_secret': 'YOUR_CLIENT_SECRET',
'redirect_uri': 'https://your-app.com/callback',
'scopes': 'payments:read payments:write fiscal:read',
'state': generate_random_state(),
'response_type': 'code'
}
return f"https://app.nopos.it/auth?{urlencode(params)}"
def generate_random_state():
return secrets.token_urlsafe(32)
<?php
function generateAuthUrl() {
$params = [
'client_id' => 'YOUR_CLIENT_ID',
'client_secret' => 'YOUR_CLIENT_SECRET',
'redirect_uri' => 'https://your-app.com/callback',
'scopes' => 'payments:read payments:write fiscal:read',
'state' => generateRandomState(),
'response_type' => 'code'
];
return 'https://app.nopos.it/auth?' . http_build_query($params);
}
function generateRandomState() {
return bin2hex(random_bytes(32));
}
?>
2. Gestisci il callback
- JavaScript/Express
- Python/Flask
- PHP
// Express.js example
app.get('/callback', (req, res) => {
const { access_token, token_type, expires_in, scope, 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');
}
// Salva il token
const tokenData = {
access_token,
token_type,
expires_in: parseInt(expires_in),
scope: scope.split(' '),
expires_at: Date.now() + (parseInt(expires_in) * 1000)
};
saveTokenToSession(req.session, tokenData);
res.redirect('/dashboard');
});
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 time
app = Flask(__name__)
@app.route('/callback')
def callback():
access_token = request.args.get('access_token')
scope = request.args.get('scope')
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
# Salva il token
token_data = {
'access_token': access_token,
'token_type': 'Bearer',
'expires_in': int(request.args.get('expires_in', 3600)),
'scope': scope.split(' ') if scope else [],
'expires_at': time.time() + int(request.args.get('expires_in', 3600))
}
save_token_to_session(session, token_data)
return redirect('/dashboard')
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'])) {
$accessToken = $_GET['access_token'];
$scope = $_GET['scope'];
$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;
}
// Salva il token
$tokenData = [
'access_token' => $accessToken,
'token_type' => 'Bearer',
'expires_in' => (int)($_GET['expires_in'] ?? 3600),
'scope' => $scope ? explode(' ', $scope) : [],
'expires_at' => time() + (int)($_GET['expires_in'] ?? 3600)
];
saveTokenToSession($_SESSION, $tokenData);
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 = generateAuthUrl();
}
}
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