ExPricer
exPricer is a dynamic pricing system for art works that adjusts prices based on exclusivity.
Install / Use
/learn @ikb-token/ExPricerREADME
exPricer
exPricer is a dynamic pricing system for art works that adjusts prices based on exclusivity. Buyers of a work can choose to pay more for more exclusivity, reducing (or even eliminating) any other copies for sale after their purchase.
This project includes both a dynamic pricing algorithm API as well as a ready-to-use web checkout and payment system (powered by Stripe) that uses the same dynamic pricing algorithm. The web checkout system is meant for digital artists who are looking to sell a limited number of a specific work, such as an image, a music file, or any other document, even a ZIP file (which can contain multiple files, such as a music album).
This project was developed by the International Klein Blue token collective. We are a collective of techno-artists inspired by the work of Yves Klein, in particular the questions he asked about how we value art, how art is priced, etc. Our collective is federated by a community token on the Solana blockchain with the symbol IKB.
License
<img alt="CC BY 4.0 License" src="https://mirrors.creativecommons.org/presskit/buttons/88x31/png/by.png" width="150" />This project can be freely used by all and is licensed under the Creative Commons Attribution 4.0 International license (CC BY 4.0). When using this project code, you must include attribution to "International Klein Blue (IKB) token collective - ikb-token.co" somewhere on your website.
Requirements
- A web hosting account or personal web server that has PHP 7.4 or higher
- If you wish to use the web checkout system, you will need to have a Stripe account; it's not necessary if you are interested in just using the exPricer API
Quick Start for Artists: Built-in Checkout System
If you're an artist looking to sell your digital work with dynamic pricing, follow these simple steps:
-
Download and Upload
- Download all files to your web server
- Make sure your server has PHP 7.4 or higher installed
-
Configure Your Settings
- Make a copy of the sample configuration file
.env.exampleand name the new copy.env - Fill in your settings in this
.envfile (this will include details about the digital file you are selling, the maximum number of copies you are selling, the minimum price, and technical details such as your Stripe account keys and email account). - If you don't have a Stripe account go here: https://dashboard.stripe.com/register, and to learn about where to get your Stripe account keys, refer to: https://docs.stripe.com/keys
- Make a copy of the sample configuration file
-
Set Up Your Files
- Upload all exPricer project files to your web hosting account or personal web server. We recommend putting everything in a new folder, named as you like, or simply
buy. (We decided to keep it simple, just use FTP/sFTP and copy over everything as is, in addition to your .env file of course!) - Upload your digital file to the
downloadsfolder
- Upload all exPricer project files to your web hosting account or personal web server. We recommend putting everything in a new folder, named as you like, or simply
-
Test Your Setup
- Visit the web URL of your web hosting account or web server, remembering to add the folder name you created. For example, this could look something like https://hosting-company.com/myaccount/buy
- Try a test purchase using Stripe's test card (4242 4242 4242 4242, any expiry date in the future, any 3-digit number as card validation code)
- If it's a digital work, verify that the download link works and that the same link is sent by email
- If you would like to adjust the text of the emails sent to buyers, look inside
success.php - After your test purchase(s) you can reset the sales history by deleting the
sales_state.jsonfile located in thedatafolder
Checkout page screenshot
<img alt="exPricer screenshot: Checkout page" src="https://github.com/user-attachments/assets/4f79ca6a-6187-4428-b3a1-474dacc64bfc" width="300" />How It Works
-
Pricing Logic (API + web checkout system)
- Price increases a little bit as fewer copies remain
- The last copy is priced at a premium
- A buyer choosing more exclusivity can make the price jump (as they are effectively paying you not to sell a certain number of copies that would be remaining)
- For the same set of conditions (minimum price, number of copies remaining), a physical work will be priced at a small premium compared to the calculated price if it had been a digital work; this is to compensate the artist with the time to handle shipping or buyer pick-up (notwithstanding any actual shipping costs that the artist may want to pass along later to the buyer for actual shipping).
-
Customer Experience (web checkout system)
- Customer visits your checkout page
- They see different pricing options based on exclusivity
- They select their preferred option and enter their email address
- They complete payment through Stripe
- If it is a digital item, they receive a download link on a payment success page and via email; if it is a physical item, they receive a simple success message and an email requesting their full shipping address
Important Notes
- Keep your
.envfile secure and never share it (it contains sensitive information!) - The
downloadsanddatadirectories are automatically protected from public web access - Test the system thoroughly before going live
Artist Support
If you need help setting up or using exPricer:
- Make sure you've followed all the steps in the Quick Start for Artists section
- Check that your
.envfile is properly configured - Verify that your web hosting account or personal web server has PHP 7.4 or higher installed
- Ensure that your Stripe account is properly set up (e.g. not pending verification, not blocked, etc.)
- If you're still having issues, please open an issue here on GitHub
API Documentation for Developers
This section is for those interested in the exPricer dynamic pricing API, for example to integrate exPricer into another web checkout system or a custom web app.
Installation
- Clone the repository to your web server
- Ensure PHP 7.4 or higher is installed
- The API is now ready to use
Endpoints
Health Check endpoint
GET /api/v1/health
Checks the health status of the API.
Response
{
"status": "healthy",
"version": "1.0.0",
"timestamp": "2024-03-21T12:00:00+00:00",
"service": "exPricer API"
}
Calculate Prices endpoint
POST /api/v1/calculate
Calculates prices for different exclusivity levels based on the input parameters.
Request
{
"work_type": "physical", // or "digital"
"copies_sold": 0, // number of copies already sold
"max_copies": 200, // maximum number of copies allowed
"min_price": 100 // minimum acceptable price in $
}
Response
{
"exclusivity_levels": [
{
"remaining_copies": 199, // number of copies that would be left for sale after this purchase
"price": 120,
"percentage_of_edition": 99.5, // percentage of edition that would be left after this purchase
"is_last_copy": false
},
// ... more exclusivity levels ...
],
"current_state": {
"total_copies": 200,
"copies_sold": 0,
"copies_remaining": 200,
"work_type": "physical",
"min_price": 100,
"work_type_factor": 1.2,
"current_market_price": 120
}
}
Error Responses
Invalid Input (400)
{
"error": "Invalid input",
"message": "Missing required field: work_type"
}
Method Not Allowed (405)
{
"error": "Method not allowed. Only POST requests are accepted."
}
Internal Server Error (500)
{
"error": "Internal server error",
"message": "Error details..."
}
API Call Sample Code
cURL
# Health check
curl http://your-domain/api/v1/health
# Calculate prices
curl -X POST http://your-domain/api/v1/calculate \
-H "Content-Type: application/json" \
-d '{
"work_type": "physical",
"copies_sold": 0,
"max_copies": 100,
"min_price": 100
}'
PHP
<?php
class ExPricerClient {
private string $baseUrl;
public function __construct(string $baseUrl) {
$this->baseUrl = rtrim($baseUrl, '/');
}
public function checkHealth(): array {
$response = $this->makeRequest('GET', '/api/v1/health');
return json_decode($response, true);
}
public function calculatePrices(
string $workType,
int $copiesSold,
int $maxCopies,
float $minPrice
): array {
$data = [
'work_type' => $workType,
'copies_sold' => $copiesSold,
'max_copies' => $maxCopies,
'min_price' => $minPrice
];
$response = $this->makeRequest('POST', '/api/v1/calculate', $data);
return json_decode($response, true);
}
private function makeRequest(string $method, string $endpoint, ?array $data = null): string {
$ch = curl_init($this->baseUrl . $endpoint);
$options = [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => ['Content-Type: application/json']
];
if ($data !== null) {
$options[CURLOPT_POSTFIELDS] = json_encode($data);
}
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new RuntimeException('API request failed: ' . curl_error($ch));
}
curl_close($ch);
if ($httpCode >= 400) {
$error = json_decode($response, true);
throw new RuntimeException(
$
