Programació carret de la compra PayPal

De Wikijoan
Dreceres ràpides: navegació, cerca

Contingut

Documentació oficial de Paypal

Comptes de Sandbox

Entorno de pruebas PayPal Sandbox

El entorno de pruebas le permite probar toda la integración antes de enviar transacciones al entorno activo de PayPal. Cree y administre cuentas de prueba, y vea correos electrónicos y credenciales de API para esas cuentas de prueba.

Para obtener más información, consulte la Guía del usuario del entorno de pruebas o Entorno de pruebas PayPal Sandbox: tutorial de introducción.

El compte de Paypal que tinc és joanqc@gmail.com/jq********, però aquest no és un compte per al sandbox. Per al Sandbox tinc:

Creo dos tests accounts: un de comprador i l'altre de venedor (Create a Sandbox Test Account)

Comprador
----------
Buyer (United States)
login email: joan_b / jq********
VISA
Add Bank Account (yes, 0$)
Faig submit i es crea. El mail per entrar serà:
joan_b_1276726117_per@yahoo.com. És de tipus Personal (veig que tincs encara comptes antics de prova que tenia)
Venedor
----------
Seller (Spain)
login email: joan_b / jq********
VISA
Add Bank Account (yes, 0$)
Faig submit i es crea. El mail per entrar serà:
joan_s_1276726259_biz@yahoo.com. És de tipus Business (veig que tincs encara comptes antics de prova que tenia)

API Credentials

Test Account: 	joan_s_1276726259_biz@yahoo.com  	Jun. 16, 2010 15:11:04 PDT
API Username: 	joan_s_1276726259_biz_api1.yahoo.com
API Password: 	1276726264
Signature: 	AKx-0uDCnKuhCAOjYsiK9rIjrLOWAysu-.JMKuyjYehVzgqi8xqWKclx
Test Account: 	joan_q_1215763639_biz@yahoo.com 	Jul. 11, 2008 01:07:24 PDT
API Username: 	joan_q_1215763639_biz_api1.yahoo.com
API Password: 	1215763644
Signature: 	AeAjTELgGEkq.LGUo9XXWmazZjNeAgsi09YTCW17A9JUD46aKb.mW69P
Test Account: 	joan_q_1215002029_biz@yahoo.com 	Jul. 2, 2008 05:35:10 PDT
API Username: 	joan_q_1215002029_biz_api1.yahoo.com
API Password: 	1215002110
Signature: 	AUCGQReQntxjhp4jHqkRbjGVDXVuAeiotNsP82wCt0LJI6UlOByv9TyT

Implementació del procés de compra

Aquest codi que ve ara no és el carret de compra. Se suposa que el carret ja el tinc. El procés fa referència al check-out.

Sample Code > Integration Wizzard > Express Checkout

Faig un wizzard per generar codi per a Paypal (Paypal Integration Wizzard)

1. Code

2. Cart

Step 2. Add PayPal to your shopping cart page

2a. Insert this code snippet into the section of your code that handles shopping cart.*

<form action='expresscheckout.php' METHOD='POST'>
<input type='image' name='submit' src='https://www.paypal.com/en_US/i/btn/btn_xpressCheckout.gif' border='0' align='top' alt='Check out with PayPal'/>
</form>

2b. Download these files to your shopping cart web directory.

Put your API Username, API Password, and Signature in the paypalfunctions code file where the comments instruct you to do so.

2c. Save the total payment amount in a Session variable named "Payment_Amount". The code inside the ExpressCheckout file is designed to read from this session variable and pass as input to the API call.

3. Billing

Step 3. Add PayPal to your billing page (payment option)

3a. Insert this code snippet into the section of your code that handles billing.

<?php

require_once ("paypalfunctions.php");

if ( $PaymentOption == "PayPal")
{
	// ==================================
	// PayPal Express Checkout Module
	// ==================================

	//'------------------------------------
	//' The paymentAmount is the total value of 
	//' the shopping cart, that was set 
	//' earlier in a session variable 
	//' by the shopping cart page
	//'------------------------------------
	$paymentAmount = $_SESSION["Payment_Amount"];

	//'------------------------------------
	//' When you integrate this code 
	//' set the variables below with 
	//' shipping address details 
	//' entered by the user on the 
	//' Shipping page.
	//'------------------------------------
	$shipToName = "<<ShiptoName>>";
	$shipToStreet = "<<ShipToStreet>>";
	$shipToStreet2 = "<<ShipToStreet2>>"; //Leave it blank if there is no value
	$shipToCity = "<<ShipToCity>>";
	$shipToState = "<<ShipToState>>";
	$shipToCountryCode = "<<ShipToCountryCode>>"; // Please refer to the PayPal country codes in the API documentation
	$shipToZip = "<<ShipToZip>>";
	$phoneNum = "<<PhoneNumber>>";

	//'------------------------------------
	//' The currencyCodeType and paymentType 
	//' are set to the selections made on the Integration Assistant 
	//'------------------------------------
	$currencyCodeType = "EUR";
	$paymentType = "Sale";

	//'------------------------------------
	//' The returnURL is the location where buyers return to when a
	//' payment has been succesfully authorized.
	//'
	//' This is set to the value entered on the Integration Assistant 
	//'------------------------------------
	$returnURL = "http://www.joanillo.org/OrderConfirmPage.php";

	//'------------------------------------
	//' The cancelURL is the location buyers are sent to when they hit the
	//' cancel button during authorization of payment during the PayPal flow
	//'
	//' This is set to the value entered on the Integration Assistant 
	//'------------------------------------
	$cancelURL = "http://www.joanillo.org/OrderCancelPage.php";

	//'------------------------------------
	//' Calls the SetExpressCheckout API call
	//'
	//' The CallMarkExpressCheckout function is defined in the file PayPalFunctions.php,
	//' it is included at the top of this file.
	//'-------------------------------------------------
	$resArray = CallMarkExpressCheckout ($paymentAmount, $currencyCodeType, $paymentType, $returnURL,
										  $cancelURL, $shipToName, $shipToStreet, $shipToCity, $shipToState,
										  $shipToCountryCode, $shipToZip, $shipToStreet2, $phoneNum
	);

	$ack = strtoupper($resArray["ACK"]);
	if($ack=="SUCCESS" || $ack=="SUCCESSWITHWARNING")
	{
		$token = urldecode($resArray["TOKEN"]);
		$_SESSION['reshash']=$token;
		RedirectToPayPal ( $token );
	} 
	else  
	{
		//Display a user friendly Error on the page using any of the following error information returned by PayPal
		$ErrorCode = urldecode($resArray["L_ERRORCODE0"]);
		$ErrorShortMsg = urldecode($resArray["L_SHORTMESSAGE0"]);
		$ErrorLongMsg = urldecode($resArray["L_LONGMESSAGE0"]);
		$ErrorSeverityCode = urldecode($resArray["L_SEVERITYCODE0"]);
		
		echo "SetExpressCheckout API call failed. ";
		echo "Detailed Error Message: " . $ErrorLongMsg;
		echo "Short Error Message: " . $ErrorShortMsg;
		echo "Error Code: " . $ErrorCode;
		echo "Error Severity Code: " . $ErrorSeverityCode;
	}
}
else
{
	if ((( $PaymentOption == "Visa") || ( $PaymentOption == "MasterCard") || ($PaymentOption == "Amex") || ($PaymentOption == "Discover"))
			&& ( $PaymentProcessorSelected == "PayPal Direct Payment"))

	//'------------------------------------
	//' The paymentAmount is the total value of 
	//' the shopping cart, that was set 
	//' earlier in a session variable 
	//' by the shopping cart page
	//'------------------------------------
	$paymentAmount = $_SESSION["Payment_Amount"];

	//'------------------------------------
	//' The currencyCodeType and paymentType 
	//' are set to the selections made on the Integration Assistant 
	//'------------------------------------
	$currencyCodeType = "EUR";
	$paymentType = "Sale";
	
	//' Set these values based on what was selected by the user on the Billing page Html form
	
	$creditCardType 		= "<<Visa/MasterCard/Amex/Discover>>"; //' Set this to one of the acceptable values (Visa/MasterCard/Amex/Discover) match it to what was selected on your Billing page
	$creditCardNumber 		= "<<CC number>>"; //' Set this to the string entered as the credit card number on the Billing page
	$expDate 				= "<<Expiry Date>>"; //' Set this to the credit card expiry date entered on the Billing page
	$cvv2 					= "<<cvv2>>"; //' Set this to the CVV2 string entered on the Billing page 
	$firstName 				= "<<firstName>>"; //' Set this to the customer's first name that was entered on the Billing page 
	$lastName 				= "<<lastName>>"; //' Set this to the customer's last name that was entered on the Billing page 
	$street 				= "<<street>>"; //' Set this to the customer's street address that was entered on the Billing page 
	$city 					= "<<city>>"; //' Set this to the customer's city that was entered on the Billing page 
	$state 					= "<<state>>"; //' Set this to the customer's state that was entered on the Billing page 
	$zip 					= "<<zip>>"; //' Set this to the zip code of the customer's address that was entered on the Billing page 
	$countryCode 			= "<<PayPal Country Code>>"; //' Set this to the PayPal code for the Country of the customer's address that was entered on the Billing page 
	$currencyCode 			= "<<PayPal Currency Code>>"; //' Set this to the PayPal code for the Currency used by the customer 
	
	/*	
	'------------------------------------------------
	' Calls the DoDirectPayment API call
	'
	' The DirectPayment function is defined in PayPalFunctions.php included at the top of this file.
	'-------------------------------------------------
	*/
	
	$resArray = DirectPayment ( $paymentType, $paymentAmount, $creditCardType, $creditCardNumber,
							$expDate, $cvv2, $firstName, $lastName, $street, $city, $state, $zip, 
							$countryCode, $currencyCode ); 

	$ack = strtoupper($resArray["ACK"]);
	if($ack=="SUCCESS" || $ack=="SUCCESSWITHWARNING")
	{
		//Getting transaction ID from API responce. 
		$TransactionID = urldecode9$resArray["TRANSACTIONID"]);
		
		echo "Your payment has been successfully processed";
	}
	else
	{
		//Display a user friendly Error on the page using any of the following error information returned by PayPal
		$ErrorCode = urldecode($resArray["L_ERRORCODE0"]);
		$ErrorShortMsg = urldecode($resArray["L_SHORTMESSAGE0"]);
		$ErrorLongMsg = urldecode($resArray["L_LONGMESSAGE0"]);
		$ErrorSeverityCode = urldecode($resArray["L_SEVERITYCODE0"]);
		
		echo "Direct credit card payment API call failed. ";
		echo "Detailed Error Message: " . $ErrorLongMsg;
		echo "Short Error Message: " . $ErrorShortMsg;
		echo "Error Code: " . $ErrorCode;
		echo "Error Severity Code: " . $ErrorSeverityCode;
	}
}
?>

3b. To pass the shipping address entered, replaced the shipping address placeholders in the code snippet with variables representing the shipping address from PayPal.

3c. Save the total payment amount in a session variable named "Payment_Amount". The code inside the ExpressCheckout file is designed to read from this session variable and pass as input to the API call.

4. Review

Step 4. Order Review - get shipping address from PayPal

4a. Insert this code snippet into the section of your code that handles order review.*

<?php
/*==================================================================
 PayPal Express Checkout Call
 ===================================================================
*/
// Check to see if the Request object contains a variable named 'token'	
$token = "";
if (isset($_REQUEST['token']))
{
	$token = $_REQUEST['token'];
}

// If the Request object contains the variable 'token' then it means that the user is coming from PayPal site.	
if ( $token != "" )
{

	require_once ("paypalfunctions.php");

	/*
	'------------------------------------
	' Calls the GetExpressCheckoutDetails API call
	'
	' The GetShippingDetails function is defined in PayPalFunctions.jsp
	' included at the top of this file.
	'-------------------------------------------------
	*/
	

	$resArray = GetShippingDetails( $token );
	$ack = strtoupper($resArray["ACK"]);
	if( $ack == "SUCCESS" || $ack == "SUCESSWITHWARNING") 
	{
		/*
		' The information that is returned by the GetExpressCheckoutDetails call should be integrated by the partner into his Order Review 
		' page		
		*/
		$email 				= $resArray["EMAIL"]; // ' Email address of payer.
		$payerId 			= $resArray["PAYERID"]; // ' Unique PayPal customer account identification number.
		$payerStatus		= $resArray["PAYERSTATUS"]; // ' Status of payer. Character length and limitations: 10 single-byte alphabetic characters.
		$salutation			= $resArray["SALUTATION"]; // ' Payer's salutation.
		$firstName			= $resArray["FIRSTNAME"]; // ' Payer's first name.
		$middleName			= $resArray["MIDDLENAME"]; // ' Payer's middle name.
		$lastName			= $resArray["LASTNAME"]; // ' Payer's last name.
		$suffix				= $resArray["SUFFIX"]; // ' Payer's suffix.
		$cntryCode			= $resArray["COUNTRYCODE"]; // ' Payer's country of residence in the form of ISO standard 3166 two-character country codes.
		$business			= $resArray["BUSINESS"]; // ' Payer's business name.
		$shipToName			= $resArray["SHIPTONAME"]; // ' Person's name associated with this address.
		$shipToStreet		= $resArray["SHIPTOSTREET"]; // ' First street address.
		$shipToStreet2		= $resArray["SHIPTOSTREET2"]; // ' Second street address.
		$shipToCity			= $resArray["SHIPTOCITY"]; // ' Name of city.
		$shipToState		= $resArray["SHIPTOSTATE"]; // ' State or province
		$shipToCntryCode	= $resArray["SHIPTOCOUNTRYCODE"]; // ' Country code. 
		$shipToZip			= $resArray["SHIPTOZIP"]; // ' U.S. Zip code or other country-specific postal code.
		$addressStatus 		= $resArray["ADDRESSSTATUS"]; // ' Status of street address on file with PayPal   
		$invoiceNumber		= $resArray["INVNUM"]; // ' Your own invoice or tracking number, as set by you in the element of the same name in SetExpressCheckout request .
		$phonNumber			= $resArray["PHONENUM"]; // ' Payer's contact telephone number. Note:  PayPal returns a contact telephone number only if your Merchant account profile settings require that the buyer enter one. 
	} 
	else  
	{
		//Display a user friendly Error on the page using any of the following error information returned by PayPal
		$ErrorCode = urldecode($resArray["L_ERRORCODE0"]);
		$ErrorShortMsg = urldecode($resArray["L_SHORTMESSAGE0"]);
		$ErrorLongMsg = urldecode($resArray["L_LONGMESSAGE0"]);
		$ErrorSeverityCode = urldecode($resArray["L_SEVERITYCODE0"]);
		
		echo "GetExpressCheckoutDetails API call failed. ";
		echo "Detailed Error Message: " . $ErrorLongMsg;
		echo "Short Error Message: " . $ErrorShortMsg;
		echo "Error Code: " . $ErrorCode;
		echo "Error Severity Code: " . $ErrorSeverityCode;
	}
}
		
?>

4b. Display the shipping address returned in the code variables on your order review page.

4c. Save the total payment amount in a session variable named "Payment_Amount". The code inside the ExpressCheckout file is designed to read from this session variable and pass as input to the API call.

5. Confirm

Step 5. Order Confirmation - confirm payment from PayPal

5a. Insert this code snippet into the section of your code that handles order confirmation.*

<?php
	/*==================================================================
	 PayPal Express Checkout Call
	 ===================================================================
	*/
require_once ("paypalfunctions.php");

if ( $PaymentOption == "PayPal" )
{
	/*
	'------------------------------------
	' The paymentAmount is the total value of 
	' the shopping cart, that was set 
	' earlier in a session variable 
	' by the shopping cart page
	'------------------------------------
	*/
	
	$finalPaymentAmount =  $_SESSION["Payment_Amount"];
		
	/*
	'------------------------------------
	' Calls the DoExpressCheckoutPayment API call
	'
	' The ConfirmPayment function is defined in the file PayPalFunctions.jsp,
	' that is included at the top of this file.
	'-------------------------------------------------
	*/

	$resArray = ConfirmPayment ( $finalPaymentAmount );
	$ack = strtoupper($resArray["ACK"]);
	if( $ack == "SUCCESS" || $ack == "SUCCESSWITHWARNING" )
	{
		/*
		'********************************************************************************************************************
		'
		' THE PARTNER SHOULD SAVE THE KEY TRANSACTION RELATED INFORMATION LIKE 
		'                    transactionId & orderTime 
		'  IN THEIR OWN  DATABASE
		' AND THE REST OF THE INFORMATION CAN BE USED TO UNDERSTAND THE STATUS OF THE PAYMENT 
		'
		'********************************************************************************************************************
		*/

		$transactionId		= $resArray["TRANSACTIONID"]; // ' Unique transaction ID of the payment. Note:  If the PaymentAction of the request was Authorization or Order, this value is your AuthorizationID for use with the Authorization & Capture APIs. 
		$transactionType 	= $resArray["TRANSACTIONTYPE"]; //' The type of transaction Possible values: l  cart l  express-checkout 
		$paymentType		= $resArray["PAYMENTTYPE"];  //' Indicates whether the payment is instant or delayed. Possible values: l  none l  echeck l  instant 
		$orderTime 			= $resArray["ORDERTIME"];  //' Time/date stamp of payment
		$amt				= $resArray["AMT"];  //' The final amount charged, including any shipping and taxes from your Merchant Profile.
		$currencyCode		= $resArray["CURRENCYCODE"];  //' A three-character currency code for one of the currencies listed in PayPay-Supported Transactional Currencies. Default: USD. 
		$feeAmt				= $resArray["FEEAMT"];  //' PayPal fee amount charged for the transaction
		$settleAmt			= $resArray["SETTLEAMT"];  //' Amount deposited in your PayPal account after a currency conversion.
		$taxAmt				= $resArray["TAXAMT"];  //' Tax charged on the transaction.
		$exchangeRate		= $resArray["EXCHANGERATE"];  //' Exchange rate if a currency conversion occurred. Relevant only if your are billing in their non-primary currency. If the customer chooses to pay with a currency other than the non-primary currency, the conversion occurs in the customer’s account.
		
		/*
		' Status of the payment: 
				'Completed: The payment has been completed, and the funds have been added successfully to your account balance.
				'Pending: The payment is pending. See the PendingReason element for more information. 
		*/
		
		$paymentStatus	= $resArray["PAYMENTSTATUS"]; 

		/*
		'The reason the payment is pending:
		'  none: No pending reason 
		'  address: The payment is pending because your customer did not include a confirmed shipping address and your Payment Receiving Preferences is set such that you want to manually accept or deny each of these payments. To change your preference, go to the Preferences section of your Profile. 
		'  echeck: The payment is pending because it was made by an eCheck that has not yet cleared. 
		'  intl: The payment is pending because you hold a non-U.S. account and do not have a withdrawal mechanism. You must manually accept or deny this payment from your Account Overview. 		
		'  multi-currency: You do not have a balance in the currency sent, and you do not have your Payment Receiving Preferences set to automatically convert and accept this payment. You must manually accept or deny this payment. 
		'  verify: The payment is pending because you are not yet verified. You must verify your account before you can accept this payment. 
		'  other: The payment is pending for a reason other than those listed above. For more information, contact PayPal customer service. 
		*/
		
		$pendingReason	= $resArray["PENDINGREASON"];  

		/*
		'The reason for a reversal if TransactionType is reversal:
		'  none: No reason code 
		'  chargeback: A reversal has occurred on this transaction due to a chargeback by your customer. 
		'  guarantee: A reversal has occurred on this transaction due to your customer triggering a money-back guarantee. 
		'  buyer-complaint: A reversal has occurred on this transaction due to a complaint about the transaction from your customer. 
		'  refund: A reversal has occurred on this transaction because you have given the customer a refund. 
		'  other: A reversal has occurred on this transaction due to a reason not listed above. 
		*/
		
		$reasonCode		= $resArray["REASONCODE"];   
	}
	else  
	{
		//Display a user friendly Error on the page using any of the following error information returned by PayPal
		$ErrorCode = urldecode($resArray["L_ERRORCODE0"]);
		$ErrorShortMsg = urldecode($resArray["L_SHORTMESSAGE0"]);
		$ErrorLongMsg = urldecode($resArray["L_LONGMESSAGE0"]);
		$ErrorSeverityCode = urldecode($resArray["L_SEVERITYCODE0"]);
		
		echo "GetExpressCheckoutDetails API call failed. ";
		echo "Detailed Error Message: " . $ErrorLongMsg;
		echo "Short Error Message: " . $ErrorShortMsg;
		echo "Error Code: " . $ErrorCode;
		echo "Error Severity Code: " . $ErrorSeverityCode;
	}
}		
		
?>

5b. Save the total payment amount in a Session variable named "Payment_Amount". The code inside the ExpressCheckout file is designed to read from this session variable and pass as input to the API call.

5c. Process the information returned by your code and complete your order backend processing.

6. Complete

Step 6. Done


Test your code. Check the buyer experience for the two flows:

Ready to Go Live?

Get a merchant account and generate API credentials. In your code:

   * Replace the Sandbox API credentials with the live API credentials.
   * In the paypalfunctions.php file, make sure to set the Sandbox variable to false this causes the code to use PayPal live site API endpoints.

DO NOT hardcode your API credentials. Manage your API credentials securely, such as loading the credentials from a secure key store or some other secure configuration.

Implementació a la pràctica

Comencem a implementar tot això. Creo el fitxer compra.php. Fico les credencials al fitxer paypalfunctions.php

Fatal error: Call to undefined function curl_init() in /home/joan/blogjoanillo/paypal/paypalfunctions.php on line 318

CURL is a communication layer required to have your webserver talk to PayPal in order to process transactions.

You're getting "curl_init" errors because your webserver doesn't have CURL support installed.

You'll need to talk to your hosting company to have them make CURL available, or find another host that does ... or don't use the PayPal Express Checkout module.

$ sudo apt-get install curl libcurl3 libcurl3-dev php5-curl

però això em la meva màquina Ubuntu 7.10 no ho puc fer, tinc massa dependències per complir. A l'institut sí que funciona, però libcurl3-dev no té candidat per a la instal.lació (no existeix el paquet).

Implementació maig 2013 (jmining, cetus data)

Hi ha una nova API, la REST API, però de moment no es pot utilitzar a l'Estat, només als EEUU.

Quan la REST API estigui disponible, en principi s'haurà de fer la migració, però en qualsevol cas hi haurà un periode de convivència. De moment, he d'utilitzar la API clàssica.


Tinc dos comptes reals a Paypal. Un és el que ja utilitzava com a comprador (joanqc@gmail.com), i l'altra el creo ara com a venedor, que ha de ser de tipus business (joan_quintana@yahoo.com):

Id. de cuenta de comprador	
GT76RR2SJSNJE -> joanqc@gmail.com (és el meu compte personal com a comprador, no l'he de fer servir per vendre)

Id. de cuenta de vendedor (business)
EJZ5XDVTMP2D2 -> joan_quintana@yahoo.com

però això en principi no ho necessitaré per a res.

Tinc dos comptes (comptes reals). Per programar són necessaris dos comptes perquè un ha de fer de venedor i l'altre de comprador:

  1. comprador: joanqc@gmail.com / jq******** (cuenta personal)
  2. venedor: joan_quintana@yahoo.com / jq******** (cuenta business, que dóna accés al sandbox)

A paypal no es poden tenir dos comptes personals (com a molt s'ha de tenir un compte personal i un altre business)

solució pasarela integral: no és la que s'adapta al meu cas. Està pensada per negocis de 50000 euros que fa temps que treballen amb paypal.

La solució que jo necessito és pago estandar o pago express. I el que jo necessito és pago express (retorna a la web meva, el que necessito jo, i és més completa i programable que el pago estandar). Hi ha un quadre comparatiu entre pago estandario pago express

El sandbox està a:

Em logo com a joan_quintana@yahoo.com (doncs aquest és el compte de business/developer)

en el sandbox creo dos accounts: un com a comprador (joanqc_buyer@gmail.com) i l'altre com a vendedor (business) (joanqc_venedor@gmail.com).

Ara que ja he creat els comptes necessito les credencials del venedor:

Username: joanqc_venedor_api1.gmail.com
Password: 1368609916
Signature: ApEaZoQ5K.Xdh87hX8cNIucFbheyADUwgHubXy4DSAfCLkIsPB38-27w

i ja puc anar al sandbox:

joanqc_venedor@gmail.com / jq**** (entro amb l'usuari del sandbox, no entro amb l'usuari real)

En el perfil veig el meu

però això en principi no ho necessitaré per a res.

Ara ja puc començar a programar.

PayPal Express Checkout with PHP

M'ha costat bastant trobar un exemple per començar a treballar, però aquest funciona bé i serà el punt de partida. D'entrada no funcionava, perquè falta:

$ sudo apt-get install curl libcurl3 libcurl3-dev php5-curl

i reiniciar el servidor apache.

i efectivament ja puc fer vendes en el sandbox.


creat per Joan Quintana Compte, juny 2010, maig 2013

Eines de l'usuari
Espais de noms
Variants
Accions
Navegació
IES Jaume Balmes
Màquines recreatives
CNC
Informàtica musical
joanillo.org Planet
Eines